c# and java - difference between hmacsha256 hash

Greg Tarr picture Greg Tarr · Nov 5, 2012 · Viewed 9k times · Source

I have the following code in Java:

byte[] secretKey = secretAccessKey.getBytes("UTF-8");
SecretKeySpec signingKey = new SecretKeySpec(secretKey, "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(signingKey);
byte[] bytes = data.getBytes("UTF-8");
byte[] rawHmac = mac.doFinal(bytes);
String result = javax.xml.bind.DatatypeConverter.printBase64Binary(rawHmac);

and the following code in C#:

UTF8Encoding enc = new UTF8Encoding();
byte[] secretKey = enc.GetBytes(secretAccessKey);
HMACSHA256 hmac = new HMACSHA256(secretKey);
hmac.Initialize();
byte[] bytes = enc.GetBytes(data);
byte[] rawHmac = hmac.ComputeHash(bytes);
string result = Convert.ToBase64String(rawHmac);

The byte arrays "secretKey" and "bytes" are equivalent but the byte array "rawHmac" is different, and the string "result" is different. Can anyone see why?

Answer

Jon Skeet picture Jon Skeet · Nov 5, 2012

Don't do this:

byte[] bytes = data.getBytes();

That will use the platform default encoding to convert a string to a byte array. That can vary between platform, whereas you want something repeatable. I would suggest UTF-8:

byte[] bytes = data.getBytes("UTF-8");

(Do the same for the key, of course.)

You should then use the same encoding in your C# - not ASCII, unless you really want to not handle non-ASCII characters.

byte[] bytes = Encoding.UTF8.GetBytes(data);

It's also not clear how you're comparing the results afterwards - don't forget that byte is signed in Java, but unsigned in C#. It's probably simplest to convert the hash to hex or base64 for comparison purposes.

EDIT: I strongly suspect the last part was the problem - comparing the results.

Here are two short but complete programs (using the iharder.net base64 converter in Java) which produce the same base64 output:

Java:

import java.util.*;
import javax.crypto.*;
import javax.crypto.spec.*;

public class Test {
    public static void main (String[] args) throws Exception {
        String secretAccessKey = "mykey";
        String data = "my data";
        byte[] secretKey = secretAccessKey.getBytes();
        SecretKeySpec signingKey = new SecretKeySpec(secretKey, "HmacSHA256");
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(signingKey);
        byte[] bytes = data.getBytes();
        byte[] rawHmac = mac.doFinal(bytes);
        System.out.println(Base64.encodeBytes(rawHmac));
    }
}

C#:

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

class Test
{
    static void Main()
    {
        String secretAccessKey = "mykey";
        String data = "my data";
        byte[] secretKey = Encoding.UTF8.GetBytes(secretAccessKey);
        HMACSHA256 hmac = new HMACSHA256(secretKey);
        hmac.Initialize();
        byte[] bytes = Encoding.UTF8.GetBytes(data);
        byte[] rawHmac = hmac.ComputeHash(bytes);
        Console.WriteLine(Convert.ToBase64String(rawHmac));
    }
}

Output from both:

ivEyFpkagEoghGnTw/LmfhDOsiNbcnEON50mFGzW9/w=