Calculating HMACSHA256 using c# to match payment provider example

后端 未结 6 1520
无人共我
无人共我 2020-12-02 06:42

For a payment provider, I need to calculate a hash-based message authentication code, using HMAC-SHA256. That is causing me quite a bit of trouble.

The payment provi

相关标签:
6条回答
  • 2020-12-02 07:23

    I realize the question is answered, but I am posting this in case others need it. Here is a snippet of code created by the payment provider (DIBS):

        /**
        * calculateMac
        * Calculates the MAC key from a Dictionary<string, string> and a secret key
        * @param params_dict The Dictionary<string, string> object containing all keys and their values for MAC calculation
        * @param K_hexEnc String containing the hex encoded secret key from DIBS Admin
        * @return String containig the hex encoded MAC key calculated
        **/
        public static string calculateMac(Dictionary<string, string> paramsDict, string kHexEnc)
        {
            //Create the message for MAC calculation sorted by the key
            var keys = paramsDict.Keys.ToList();
            keys.Sort();
            var msg = "";
            foreach (var key in keys)
            {
                if (key != keys[0]) msg += "&";
                msg += key + "=" + paramsDict[key];
            }
    
            //Decoding the secret Hex encoded key and getting the bytes for MAC calculation
            var kBytes = new byte[kHexEnc.Length / 2];
            for (var i = 0; i < kBytes.Length; i++)
            {
                kBytes[i] = byte.Parse(kHexEnc.Substring(i * 2, 2), NumberStyles.HexNumber);
            }
    
            //Getting bytes from message
            var msgBytes = Encoding.Default.GetBytes(msg);
    
            //Calculate MAC key
            var hash = new HMACSHA256(kBytes);
            var macBytes = hash.ComputeHash(msgBytes);
            var mac = BitConverter.ToString(macBytes).Replace("-", "").ToLower();
    
            return mac;
        }
    

    http://tech.dibspayment.com/DX/Hosted/HMAC

    0 讨论(0)
  • 2020-12-02 07:24

    A SHA hash is calculated on a sequence of bytes. Bytes are a profoundly different datatype to characters. You should not use character Strings to store binary data such as hashes.

    sb.Append(Convert.ToString(Convert.ToChar(Int32.Parse(hex.Substring(i, 2)...

    This creates a character string by reading each encoded byte and turning into a character of the same Unicode code point number. This is equivalent to decoding the bytes 0-255 using the ISO-8859-1 (Latin1) encoding, due to that encoding's property of matching the first 256 code points in Unicode.

    var enc = Encoding.Default; [...] baSalt = enc.GetBytes(salt);

    byte[] sha256Bytes = Encoding.Default.GetBytes(input);

    These both convert the characters back to bytes using the system default encoding. This encoding varies between installs, but it will never be ISO-8859-1 - even the similar Western European code page 1252 has different characters in the range 0x80-0x9F.

    Consequently the byte array you are using doesn't contain the bytes implied by the example hex sequences. A cheap fix would be to use Encoding.GetEncoding("ISO-8859-1") instead of the default encoding, but really you should be using a bytes array to store data in the first place instead of a String, eg:

    byte[] key= new byte[] { 0x57, 0x61, 0x7b, 0x5d, 0x23, 0x49, ... };
    

    and pass that directly into ComputeHash.

    If you must initialise data from a hex string, parse it directly into a byte array, eg:

    private static byte[] HexDecode(string hex) {
        var bytes= new byte[hex.Length/2];
        for (int i= 0; i<bytes.Length; i++) {
            bytes[i]= byte.Parse(hex.Substring(i*2, 2), NumberStyles.HexNumber);
        }
        return bytes;
    }
    
    0 讨论(0)
  • 2020-12-02 07:27

    Thanks you saved my time.

    request.Method = "GET";
    string signature = "";
    string strtime = DateTime.UtcNow.ToString("yyyy-MM-ddTHH\\:mm\\:ssZ");
    
    string secret = "xxxx";
    
    string message = "sellerid:email:" + strtime; 
    
    var encoding = new System.Text.ASCIIEncoding(); 
    
    byte[] keyByte = encoding.GetBytes(secret);
    
    byte[] messageBytes = encoding.GetBytes(message);
    using (var hmacsha256 = new HMACSHA256(keyByte))
    {
    var hash = new HMACSHA256(keyByte);
    byte[] signature1 = hash.ComputeHash(messageBytes);
    signature = BitConverter.ToString(signature1).Replace("-", "").ToLower();
    }
    
    request.Headers.Add("authorization", "HMAC-SHA256" + " " + 
    "emailaddress=xxx@xx.com,timestamp=" + strtime + ",signature=" + signature);
    HttpWebResponse response = request.GetResponse() as HttpWebResponse;
    
    0 讨论(0)
  • 2020-12-02 07:38

    Here's a string extension method for getting a fairly standard HMAC SHA 256 token for a given string:

    usage:

    myMessageString.HmacSha256Digest(mySecret)
    

    string extension method:

    public static string HmacSha256Digest(this string message, string secret)
    {
        ASCIIEncoding encoding = new ASCIIEncoding();
        byte[] keyBytes = encoding.GetBytes(secret);
        byte[] messageBytes = encoding.GetBytes(message);
        System.Security.Cryptography.HMACSHA256 cryptographer = new System.Security.Cryptography.HMACSHA256(keyBytes);
    
        byte[] bytes = cryptographer.ComputeHash(messageBytes);
    
        return BitConverter.ToString(bytes).Replace("-", "").ToLower();
    }
    
    0 讨论(0)
  • 2020-12-02 07:39

    I've made a complete solution to your issue (since that is probably what you were looking for). It calculates the correct hash using both your method 1 and 2.

    Overview

    The program can be organized in to three sections:

    1. Hash functions - these are the actual functions that will calculate the hashes using byte[] for input
    2. Encoding helpers - these are used with the the hash hex functions (#3) and help with converting the following:
      • string -> byte[]
      • byte[] -> hex string
      • hex string -> byte[] (thanks @bobince!)
    3. Hash hex functions - these are helper functions so that you can use the hash functions (#1) using hex string as input instead. These use the encoding helpers (#2) to do that.

    Code

    0. Using Statements

    Before you get started, make sure to that you have the following using statements so that you don't get a ton of errors from not including them.

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

    1. Hash functions

    HMAC-SHA256 (Method 1)

    This will calculate the HMAC-SHA256 (your method 1). As you can see, it is much simpler than method 2 but gives the same result.

    private static byte[] HashHMAC(byte[] key, byte[] message)
    {
        var hash = new HMACSHA256(key);
        return hash.ComputeHash(message);
    }
    

    SHA256 (Method 2)

    Now to calculate the hash using a ton of SHA hashing (your method 2), it is a little bit more involved. This is basically the same as your pseudo-code without the hex decoding and uses byte[] for input instead. This would look like:

    MAC = SHA256( outerKey + SHA256( innerKey + message ) )
    

    Instead of your:

    MAC = SHA256( hexDecode(outerKey) + SHA256( hexDecode(innerKey) + message ) )
    

    Where outerKey, innerKey, and message are all byte[]s. Of course, in this case, all the keys have already been decoded from hexadecimal strings but it may as well been byte[]s too.

    So the code can be broken down into these steps:

    1. Create the buffer for the inner data and store it in byte[] innerData
    2. Copy the innerKey and the message to the byte[] innerData
    3. Now compute the SHA256 hash of innerData and store it in byte[] innerHash
    4. For the final and entire hash, create a buffer for it in byte[] data
    5. Copy the outerKey and innerHash, the previously computed hash (from #3), to the data
    6. Compute the final hash of data and store it in result and return it.

    To do the byte copying I'm using the Buffer.BlockCopy() function since it apparently faster than some other ways (source). Those steps then can be written in code like this:

    private static byte[] HashSHA(byte[] innerKey, byte[] outerKey, byte[] message)
    {
        var hash = new SHA256Managed();
    
        // Compute the hash for the inner data first
        byte[] innerData = new byte[innerKey.Length + message.Length];
        Buffer.BlockCopy(innerKey, 0, innerData, 0, innerKey.Length);
        Buffer.BlockCopy(message, 0, innerData, innerKey.Length, message.Length);
        byte[] innerHash = hash.ComputeHash(innerData);
    
        // Compute the entire hash
        byte[] data = new byte[outerKey.Length + innerHash.Length];
        Buffer.BlockCopy(outerKey, 0, data, 0, outerKey.Length);
        Buffer.BlockCopy(innerHash, 0, data, outerKey.Length, innerHash.Length);
        byte[] result = hash.ComputeHash(data);
    
        return result;
    }
    

    2. Helper functions

    Before we get to the hash hex function, you need a few functions to help with converting between things as said in the overview.

    string -> byte[]

    The string encoding assumes the text is plain ASCII and seems to work (for now). Though, if you need to encode with fancy symbols, you are probably going to need to use UTF8 instead. If that is the case, then switch out ASCIIEncoding with UTF8Encoding or whatever encoding you're using.

    private static byte[] StringEncode(string text)
    {
        var encoding = new ASCIIEncoding();
        return encoding.GetBytes(text);
    }
    

    byte[] -> hex string

    All this does is take an array of bytes and turn it to a lower-case hex string. Pretty simple.

    private static string HashEncode(byte[] hash)
    {
        return BitConverter.ToString(hash).Replace("-", "").ToLower();
    }
    

    hex string -> byte[]

    Lastly is the conversion of a hex string to a byte array. This came from @bobince's answer so it's not mine. Giving credit where credit is due.

    private static byte[] HexDecode(string hex)
    {
        var bytes = new byte[hex.Length / 2];
        for (int i = 0; i < bytes.Length; i++)
        {
            bytes[i] = byte.Parse(hex.Substring(i * 2, 2), NumberStyles.HexNumber);
        }
        return bytes;
    }
    

    3. Hash hex functions

    As said before, these are the helper functions that work with the hash functions with hex data and strings instead. They are pretty self-explanatory:

    Hex hashing for HMAC

    private static string HashHMACHex(string keyHex, string message)
    {
        byte[] hash = HashHMAC(HexDecode(keyHex), StringEncode(message));
        return HashEncode(hash);
    }
    

    Hex hashing for SHA

    private static string HashSHAHex(string innerKeyHex, string outerKeyHex, string message)
    {
        byte[] hash = HashSHA(HexDecode(innerKeyHex), HexDecode(outerKeyHex), StringEncode(message));
        return HashEncode(hash);
    }
    

    4. Console Test

    Well to wrap all the functions together, here is a console program that will call the functions to show that they are actually working properly.

    static void Main(string[] args)
    {
        string message = "amount=100&currency=EUR";
        string expectedHex = "b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905";
        Console.WriteLine("Ref : " + expectedHex);
    
        // Test out the HMAC hash method
        string key = "57617b5d2349434b34734345635073433835777e2d244c31715535255a366773755a4d70532a5879793238235f707c4f7865753f3f446e633a21575643303f66";
        string hashHMACHex = HashHMACHex(key, message);
        Console.WriteLine("HMAC: " + hashHMACHex);
    
        // Test out the SHA hash method
        string innerKey = "61574d6b157f757d02457573556645750e0341481b127a07476303136c005145436c7b46651c6e4f4f040e1569464a794e534309097258550c17616075060950";
        string outerKey = "0b3d27017f151f17682f1f193f0c2f1f64692b227178106d2d096979066a3b2f2906112c0f760425256e647f032c2013243929636318323f667d0b0a1f6c633a";
        string hashSHAHex = HashSHAHex(innerKey, outerKey, message);
        Console.WriteLine("SHA : " + hashSHAHex);
    
        Console.ReadLine();
    }
    

    If everything went correctly and it ran without errors, you should get the following output showing that all the hashes are correct (ref is the expected hash):

    Ref : b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
    HMAC: b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
    SHA : b436e3e86cb3800b3864aeecc8d06c126f005e7645803461717a8e4b2de3a905
    

    Conclusion

    Lastly, just to make sure everything worked, the code altogether can be found at:
    http://pastebin.com/xAAuZrJX

    0 讨论(0)
  • 2020-12-02 07:45

    You can use this method for HMACSHA256.

    string key = "your key";
    string message = "your message";
    System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
    byte[] keyByte = encoding.GetBytes(key);
    
    HMACSHA256 hmacsha256 = new HMACSHA256(keyByte);
    
    byte[] messageBytes = encoding.GetBytes(message);
    byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
    return ByteToString(hashmessage);
    

    Here is the ByteToString method:

    public static string ByteToString(byte[] buff)
        {
            string sbinary = "";
    
            for (int i = 0; i < buff.Length; i++)
            {
                sbinary += buff[i].ToString("X2"); // hex format
            }
            return (sbinary);
        }
    
    0 讨论(0)
提交回复
热议问题