簡體   English   中英

HMC SHA1哈希 - Java生成與C#不同的哈希輸出

[英]HMC SHA1 hash - Java producing different hash output than C#

這是對這個問題的跟進,但我正在嘗試將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();

Recurly在其簽名文檔頁面上提供以下示例數據:

未加密的驗證消息 :[1312701386,transactioncreate,[account_code:ABC,amount_in_cents:5000,貨幣:美元]]

私鑰 :0123456789ABCDEF0123456789ABCDEF

結果簽名 :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變量時,它與示例簽名的消息部分不匹配。 具體來說,我得到的值是“c8a9188dcf85d1378976729e50f1de5093fabb78”而不是“0f5630424b32402ec03800e977cd7a8b13dbd153”。

更新

Per @ M.Babcock,我用示例數據重新編寫C#代碼,並返回與Java代碼相同的輸出。 所以看來我的哈希方法是正確的,但我傳遞了錯誤的數據(unencryptedMessage)。 嘆。 如果/當我可以確定要加密的正確數據時,我將更新此帖子 - 因為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());

結果:

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));

結果:

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

Key Bytes: 4d857d2408b00c3dd17f0c4ffcf15b97f1049867
  Results: c8a9188dcf85d1378976729e50f1de5093fabb78

我懷疑你正在處理的值的默認編碼可能不同。 由於它們沒有指定,它們將使用基於您正在處理的平台的字符串的默認編碼值。

我做了一個快速搜索來驗證這是否屬實並且它仍然沒有結果,但它讓我認為.NET中的字符串默認為UTF-16編碼,而Java默認為UTF-8。 (有人可以證實這一點嗎?)

如果是這種情況,那么使用UTF-8編碼的GetBytes方法已經為每種情況產生了不同的輸出。

基於示例代碼,看起來Java希望您在創建SecretKeySpec之前尚未對您的密鑰進行SHA1。 你試過嗎?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM