Search code examples
javapowershellhmacsha1

Getting HmacSHA1 Java Hash to match Powershell Hash


I have this Powershell snippet, which I cannot change and provides the Hash I need to match:

$key = 'test'
$bytes = [Text.Encoding]::UTF8.GetBytes($key)
WRITE-HOST "bytes: " $bytes
$hmacsha = new-object system.security.cryptography.HMACSHA1
$hmacsha.key = $bytes
$hash = $hmacsha.ComputeHash($bytes)
WRITE-HOST "hash: " $hash

Which gives this result:

bytes:  116 101 115 116
hash:  12 148 81 92 21 229 9 91 138 135 165 11 160 223 59 243 142 208 95 230

And this in Java:

String key = "test";
byte[] bytes = key.getBytes("UTF-8");
System.out.println("bytes: " + Arrays.toString(bytes));

MessageDigest md = MessageDigest.getInstance("SHA-1");
byte[] keyBytes = md.digest(bytes);
SecretKey sk = new SecretKeySpec(keyBytes, "HmacSHA1");
Mac mac = Mac.getInstance("HmacSHA1");
mac.init(sk);
byte[] hash = mac.doFinal(keyBytes);
System.out.println("hash 1: " + Arrays.toString(hash));

sk = new SecretKeySpec(bytes, "HmacSHA1");
mac = Mac.getInstance("HmacSHA1");
mac.init(sk);
hash = mac.doFinal(bytes);
System.out.println("hash 2: " + Arrays.toString(hash));

Which outputs:

bytes: [116, 101, 115, 116]
hash 1: [-112, -37, 64, 65, -91, -97, 36, 60, -80, -110, 62, -105, -63, -69, -79, -18, 29, -95, 23, -116]
hash 2: [12, -108, 81, 92, 21, -27, 9, 91, -118, -121, -91, 11, -96, -33, 59, -13, -114, -48, 95, -26]

I cannot seem to get the Hash to match what Powershell is outputting. In both examples I am creating a hmacsha1 instance with a key of "test", then getting the hash of "test", but both have some minor difference causing them to give different results.


Solution

  • You are not getting 2 different hashes. Matter of fact ...

    12, -108, 81, 92, 21, -27, 9, 91, -118, -121, -91, 11, -96, -33, 59, -13, -114, -48, 95, -26
    

    is the same as

    12 148 81 92 21 229 9 91 138 135 165 11 160 223 59 243 142 208 95 230
    

    What is happening is in java when you turn to string it is making these values signed.

    An example of this is with -108 and 148

    00000000,10010100 = 148
    11111111,10010100 = -108
    

    If you want to make the byte look unsigned you can use 0xFF combined with the byte.

    byte b = (byte) Integer.parseInt("148");
    System.out.println(b);
    

    outputs : -108

    byte b = (byte) Integer.parseInt("148");
    System.out.println(b & 0xFF);
    

    outputs : 148

    What is happening is you are usinga bitwise AND operator so 0xFF = 255 aka 11111111 and matching common bits and keeping them.

    00000000,11111111 = 255 aka 0xFF
    11111111,10010100 = -108
    --------
    00000000,10010100 = 148