HMC SHA1 hash - Java producing a different hash output than C #

This is a this question, but I'm trying to pass C # code in Java instead of Ruby code in C #, as was the case with the related question. I am trying to verify the encrypted signature returned from the Recurly.js api. Unfortunately, Recurly does not have a Java library to help with its verification, so I have to do a signature verification myself.

In the above question ( this ), the following C # code can generate the hash needed to verify the signature returned with Recurly:

var privateKey = Configuration.RecurlySection.Current.PrivateKey; var hashedKey = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(privateKey)); var hmac = new HMACSHA1(hashedKey); var hash = hmac.ComputeHash(Encoding.ASCII.GetBytes(dataToProtect)); return BitConverter.ToString(hash).Replace("-", "").ToLower(); 

Re-provides the following sample data on the signature page:

unencrypted verification message : [1312701386, transactioncreate, [account_code: ABC, amount_in_cents: 5000, currency: USD]]

private key : 0123456789ABCDEF0123456789ABCDEF

final signature : 0f5630424b32402ec03800e977cd7a8b13dbd153-1312701386

Here is my Java implementation:

 String unencryptedMessage = "[1312701386,transactioncreate,[account_code:ABC,amount_in_cents:5000,currency:USD]]"; String privateKey = "0123456789ABCDEF0123456789ABCDEF"; String encryptedMessage = getHMACSHA1(unencryptedMessage, getSHA1(privateKey)); private static byte[] getSHA1(String source) throws NoSuchAlgorithmException, UnsupportedEncodingException{ MessageDigest md = MessageDigest.getInstance("SHA-1"); byte[] bytes = md.digest(source.getBytes("UTF-8")); return bytes; } private static String getHMACSHA1(String baseString, byte[] keyBytes) throws GeneralSecurityException, UnsupportedEncodingException { SecretKey secretKey = new SecretKeySpec(keyBytes, "HmacSHA1"); Mac mac = Mac.getInstance("HmacSHA1"); mac.init(secretKey); byte[] bytes = baseString.getBytes("ASCII"); return Hex.encodeHexString(mac.doFinal(bytes)); } 

However, when I print out the encryptedMessage variable, it does not match the message portion of the sample signature. In particular, I get the value "c8a9188dcf85d1378976729e50f1de5093fabb78" instead of "0f5630424b32402ec03800e977cd7a8b13dbd153".

Update

Per @ M.Babcock, I reprogrammed C # code with the sample data and returned the same result as the Java code. Thus, my hashing approach is correct, but I am transmitting incorrect data (unencryptedMessage). Sigh. I will update this post if / when I can determine what the correct data for encryption is, since the “unencrypted verification message” presented in the Recurly documentation seems to be missing something.

Update 2

The error turned out to be data or the "unencrypted verification message" format. The message in the sample data does not actually encrypt the submitted sample signature, or maybe there is outdated documentation? Anyway, I confirmed that the Java implementation will work for real-world data. Thanks to everyone.

+6
source share
3 answers

I think the problem is in .NET code. Does the string Configuration.RecurlySection.Current.PrivateKey return the string? Is this the value you expect?

Using the following code, .NET and Java return identical results.

.NET code

 string message = "[1312701386,transactioncreate,[account_code:ABC,amount_in_cents:5000,currency:USD]]"; string privateKey = "0123456789ABCDEF0123456789ABCDEF"; var hashedKey = SHA1.Create().ComputeHash(Encoding.UTF8.GetBytes(privateKey)); var hmac = new HMACSHA1(hashedKey); var hash = hmac.ComputeHash(Encoding.ASCII.GetBytes(message)); Console.WriteLine(" Message: {0}", message); Console.WriteLine(" Key: {0}\n", privateKey); Console.WriteLine("Key bytes: {0}", BitConverter.ToString(hashedKey).Replace("-", "").ToLower()); Console.WriteLine(" Result: {0}", BitConverter.ToString(hash).Replace("-", "").ToLower()); 

Result:

  Message: [1312701386, transactioncreate, [account_code: ABC, amount_in_cents: 5000, currency: USD]]
       Key: 0123456789ABCDEF0123456789ABCDEF

 Key bytes: 4d857d2408b00c3dd17f0c4ffcf15b97f1049867
    Result: c8a9188dcf85d1378976729e50f1de5093fabb78

Java

 String message = "[1312701386,transactioncreate,[account_code:ABC,amount_in_cents:5000,currency:USD]]"; String privateKey = "0123456789ABCDEF0123456789ABCDEF"; MessageDigest md = MessageDigest.getInstance("SHA-1"); byte[] keyBytes = md.digest(privateKey.getBytes("UTF-8")); SecretKey sk = new SecretKeySpec(keyBytes, "HmacSHA1"); Mac mac = Mac.getInstance("HmacSHA1"); mac.init(sk); byte[] result = mac.doFinal(message.getBytes("ASCII")); System.out.println(" Message: " + message); System.out.println(" Key: " + privateKey + "\n"); System.out.println("Key Bytes: " + toHex(keyBytes)); System.out.println(" Results: " + toHex(result)); 

Result:

  Message: [1312701386, transactioncreate, [account_code: ABC, amount_in_cents: 5000, currency: USD]]
       Key: 0123456789ABCDEF0123456789ABCDEF

 Key Bytes: 4d857d2408b00c3dd17f0c4ffcf15b97f1049867
   Results: c8a9188dcf85d1378976729e50f1de5093fabb78
+5
source

I suspect that the encoding of the default values ​​you use may be different. Since they did not specify it, they will use the default encoding value for the string based on the platform you are working on.

I did a quick search to check if this was true, and it was still inconclusive, but it made me think that strings in .NET by default are encoded with UTF-16, and Java by default is encoded with UTF-8. (Can someone confirm this?)

If this is the case, then your GetBytes method with UTF-8 encoding already produces a different output for each case.

+1
source

Based on this sample code, it looks like Java expects you do not have SHA1'd of your key before creating SecretKeySpec. Have you tried this?

0
source

Source: https://habr.com/ru/post/905465/


All Articles