HMC SHA1散列-Java产生与C#不同的散列输出
本文关键字:输出 散列 SHA1 -Java HMC | 更新日期: 2023-09-27 18:19:48
这是这个问题的后续内容,但我试图将C#代码移植到Java,而不是像相关问题中那样将Ruby代码移植到C#。我正在尝试验证从Recurly.js api返回的加密签名是否有效。不幸的是,Recurly没有Java库来帮助验证,所以我必须自己实现签名验证。
根据上面的相关问题(this),以下C#代码可以生成验证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();
在其签名文档页面上定期提供以下示例数据:
未加密的验证消息:[1312701386,transactioncreate,[account_code:ABC,amount_in_cents:5000,currency:USD]]
私钥:0123456789bcdef0123456789 bcdef
结果签名:0f5630424b32402ec03800e977cd7a8b13dbd153-1312701386
这是我的Java实现:
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));
}
但是,当我打印出encryptedMessage变量时,它与示例签名的消息部分不匹配。具体来说,我得到的值是"c8a9188dcf85d1378976729e50f1d5093abb78",而不是"0f5630424b32402ec03800e977cd7a8b13dbd153"。
更新
Per@M.Babcock,我用示例数据重新编译了C#代码,它返回了与Java代码相同的输出。因此,我的哈希方法似乎是正确的,但我传递了错误的数据(未加密的消息)。叹气如果/何时我可以确定要加密的正确数据,我将更新这篇文章,因为Recurly文档中提供的"未加密验证消息"似乎缺少一些内容。
更新2
错误原来是"未加密的验证消息"数据/格式。示例数据中的消息实际上并没有加密到提供的示例签名,所以可能是过时的文档?无论如何,我已经确认Java实现将适用于真实世界的数据。谢谢大家。
我认为问题出在您的.NET代码中。Configuration.RecurlySection.Current.PrivateKey
是否返回字符串?这个值是你期望的密钥吗?
使用以下代码,.NET和Java返回相同的结果。
.NET代码
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());
结果:
消息:[1312701386,transactioncreate,[account_code:ABC,amount_in_cents:5000,currency:USD]]密钥:0123456789ABCDEF 0123456789BBCDEF密钥字节:4d857d2408b00c3dd17f0c4fcf15b97f1049867结果:c8a9188dcf85d1378976729e50f1d5093abb78
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));
结果:
消息:[1312701386,transactioncreate,[account_code:ABC,amount_in_cents:5000,currency:USD]]密钥:0123456789ABCDEF 0123456789BBCDEF密钥字节:4d857d2408b00c3dd17f0c4fcf15b97f1049867结果:c8a9188dcf85d1378976729e50f1d5093abb78
我怀疑您正在处理的值的默认编码可能不同。由于他们没有指定,他们将根据您所使用的平台使用字符串的默认编码值
我做了一个快速搜索来验证这是否是真的,但仍然没有结论,但这让我认为.NET中的字符串默认为UTF-16编码,而Java默认为UTF-8。(有人能证实吗?)
如果是这样的话,那么使用UTF-8编码的GetBytes
方法已经为每种情况生成了不同的输出。
基于此示例代码,Java似乎希望您在创建SecretKeySpec之前还没有对密钥进行SHA1'd。你试过了吗?