5

i have tried several links from stackoverflow to get HmacSHA256 with key to work with java, but i always get

 func check(body: String) -> String {
    let hash = body.hmac(HMACAlgorithm.sha256, key: Router.sigKey)
    print("SIG: " + Router.sigKey)
    print("result of hash. \(hash)")
    return hash
}

This function returns hash with key from given String. Key was: 0393e944ee8108bb66fc9fa4f99f9c862481e9e0519e18232ba61b0767eee8c6

String was: example

Result is: 27effb76c97022497e25d3a5d7e823462f212a82d9ebba35f179071568b0c335

When i use this website to check if my SHA256 is good with the same key, it returns same answer, so i know my code in swift is good. But when i try to do it in java, here is the source code.

public static String HMAC_SHA(){
    try {
        String secret = "0393e944ee8108bb66fc9fa4f99f9c862481e9e0519e18232ba61b0767eee8c6";
        String message = "example";
        Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
        SecretKeySpec secret_key = new SecretKeySpec(secret.getBytes(), "HmacSHA256");
        sha256_HMAC.init(secret_key);
        String hash = android.util.Base64.encodeToString(sha256_HMAC.doFinal(message.getBytes()), Base64.URL_SAFE);
        return new String(Hex.encodeHex(hash.getBytes()));
    }
    catch (Exception e){
        e.printStackTrace();
    }
    return null;
}

It returns this: 4a2d5f3764736c77496b6c2d4a644f6c312d676a526938684b6f4c5a36376f3138586b4846576977777a553d0a

Which is not even similar to the swift output. How can i achieve the same result with java from the swift code above, it would be helpful a lot!

Miljan Rakita
  • 1,433
  • 4
  • 23
  • 44
  • You should never use String.getBytes() without an explicit charset. Compare the actual bytes you use as input in Swift and Java application. – vanje Oct 28 '17 at 10:43
  • @vanje I will try it now, and check if ti was a problem. – Miljan Rakita Oct 28 '17 at 10:50
  • @vanje Which charset should i use ? – Miljan Rakita Oct 28 '17 at 10:51
  • It doesn't matter, but you should use the same on each system. E.g. "Cp1252" is for Windows-ANSI or try "UTF-8". – vanje Oct 28 '17 at 13:18
  • @vanje I have tried UTF-8. But it displays same hash as without UTF-8. Any other idea what could help me ? – Miljan Rakita Oct 28 '17 at 14:26
  • (1) for some/much data charset can be an issue but for the actual string here `example` it is not (2) your swift just displays the result in hex, but your java encodes to base64 and then the base64 to hex, which is both extremely silly and inconsistent; omit the base64 and you get the same result (although I suspect that key was _intended_ to be treated as hex, which you didn't do in either case) – dave_thompson_085 Aug 20 '18 at 21:18

2 Answers2

10
    String key = "0393e944ee8108bb66fc9fa4f99f9c862481e9e0519e18232ba61b0767eee8c6";
    Mac sha256_HMAC = Mac.getInstance("HmacSHA256");
    sha256_HMAC.init(new SecretKeySpec(key.getBytes(), "HmacSHA256"));
    byte[] result = sha256_HMAC.doFinal("example".getBytes());
    System.out.println (DatatypeConverter.printHexBinary(result));
    // ONLY CONVERT TO HEX (= SWIFT) NOT FIRST TO BASE64

result as requested

 27EFFB76C97022497E25D3A5D7E823462F212A82D9EBBA35F179071568B0C335
dave_thompson_085
  • 34,712
  • 6
  • 50
  • 70
-1

Your key contains values greater then the value 127 and, Mac and SecretKeySpec use bytes, which in Java can contain values from -128 to 127.


In the HmacSHA256 algorithm, the key is interpreted as a string of hexadecimal values. In the case of your secret, the decimal values of this key are: 3,147,233,68,238,129,8,187,102,252,159,164,249,159,156,134,36,129,233,224,81,158,24,35,43,166,27,7,103,238,232,198

As you can see, some of them have a value over 127. When creating the SecretKeySpec object and while doing calculations within the Mac class, Java uses byte[] to store this and related sequences. In Java, a byte can contain values from -128 to 127, which means that when storing this secret, the values > 127 will "flip" and will make sure the calculations following this will not go as you'd expect.

In the Swift case (and with C++, Ruby, and other languages), the conversion from hex to byte occurs without losing the actual value.

  • I now see the following answer, which gives even more detail: https://stackoverflow.com/questions/47797100/java-vs-golang-for-hotp-rfc-4226 – Chris Brandhorst Aug 20 '18 at 14:44
  • The problem here is the call to `getBytes()`. The `secret` String should be parsed from hexadecimal, which is a completely different thing. In the question you linked it's done correctly with `decodeHex`. – Kayaman Aug 20 '18 at 15:54
  • This is wrong. JVM treats `byte` as signed, but it's easy to make it unsigned by masking it and that's exactly what all JCE code does, and produces the correct result. In _your_ code you should also do it correctly. @Kayaman: in the other Q yes; here the key looks hex but the swift code did NOT treat it as such, and the actual cause of the difference was double-encoding the output – dave_thompson_085 Aug 20 '18 at 21:20
  • @Kayaman I'm gonna need to evaluate in my usecase I was led to this conclusion. Thanks for the pointers. – Chris Brandhorst Aug 21 '18 at 08:09