问题
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?
回答1:
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=
回答2:
This was a non-question, as demonstrated, the hashes are always the same.
The problem in my case was unrelated, the fact that Java uppercases percent encoding on UrlEncoder but .NET doesn't.
Goes to show how important it is to test in isolation!
来源:https://stackoverflow.com/questions/13237456/c-sharp-and-java-difference-between-hmacsha256-hash