2009-12-03 118 views
3

我正在實現一個名爲Suomen Verkkomaksut的數字支付服務接口。有關付款的信息通過HTML表單發送給他們。爲了確保在傳輸過程中沒有人混淆了信息,MD5哈希計算兩端的特殊密鑰並未發送給它們。MD5哈希的ISO-8859-1字符串在Java中

我的問題是,由於某些原因,他們似乎認爲傳入的數據是用ISO-8859-1而不是UTF-8編碼的。我發送給他們的散列是用UTF-8字符串計算的,因此它與他們計算的散列不同。

我試圖與以下代碼:

String prehash = "6pKF4jkv97zmqBJ3ZL8gUw5DfT2NMQ|13466|123456||Testitilaus|EUR|http://www.esimerkki.fi/success|http://www.esimerkki.fi/cancel|http://www.esimerkki.fi/notify|5.1|fi_FI|0412345678|0412345678|[email protected]|Matti|Meikäläinen||Testikatu 1|40500|Jyväskylä|FI|1|2|Tuote #101|101|1|10.00|22.00|0|1|Tuote #202|202|2|8.50|22.00|0|1"; 
String prehashIso = new String(prehash.getBytes("ISO-8859-1"), "ISO-8859-1"); 

String hash = Crypt.md5sum(prehash).toUpperCase(); 
String hashIso = Crypt.md5sum(prehashIso).toUpperCase(); 

不幸的是這兩個散列值與相同C83CF67455AF10913D54252737F30E21。根據Suomen Verkkomaksut的文檔,此示例案例的正確值爲975816A41B9EB79B18B3B4526569640E。

有沒有一種方法可以用ISO-8859-1字符串在Java中計算MD5哈希值?

更新:在等待Suomen Verkkomaksut的答案時,我找到了一種替代方法來製作哈希。 Michael Borgwardt糾正了我對String和編碼的理解,並且尋找了一種從byte []中進行散列的方法。

Apache Commons是庫的一個很好的來源,我發現他們的DigestUtils類有一個md5hex函數,它需要byte []輸入並返回一個32個字符的十六進制字符串。

由於某些原因,這仍然不起作用。這兩個返回相同的值:

DigestUtils.md5Hex(prehash.getBytes()); 
DigestUtils.md5Hex(prehash.getBytes("ISO-8859-1")); 
+0

如果您想獲得可預測的結果,請不要使用第一種形式的'getBytes()'。它使用系統的默認編碼。您來自芬蘭,您的默認編碼很可能是'ISO-8859-1',因此這兩個調用都會產生相同的結果。 – 2009-12-03 14:04:13

回答

2

Java有一個標準的java.security.MessageDigest類,用於計算不同的哈希值。

下面是示例代碼

include java.security.MessageDigest; 

// Exception handling not shown 

String prehash = ... 

final byte[] prehashBytes= prehash.getBytes("iso-8859-1"); 

System.out.println(prehash.length()); 
System.out.println(prehashBytes.length); 

final MessageDigest digester = MessageDigest.getInstance("MD5"); 

digester.update(prehashBytes); 

final byte[] digest = digester.digest(); 

final StringBuffer hexString = new StringBuffer(); 

for (final byte b : digest) { 
    final int intByte = 0xFF & b; 

    if (intByte < 10) 
    { 
     hexString.append("0"); 
    } 

    hexString.append(
     Integer.toHexString(intByte) 
    ); 
} 

System.out.println(hexString.toString().toUpperCase()); 

不幸的是,你把它產生相同的「C83CF67455AF10913D54252737F30E21」哈希值。所以,我猜你的Crypto課程是免費的。我特別添加了prehashprehashBytes長度打印輸出以驗證確實使用了「ISO-8859-1」。在這種情況下,兩者都是328.

當我做了presash.getBytes("utf-8")它產生了「9CC2E0D1D41E67BE9C2AB4AABDB6FD3」(並且字節數組的長度變爲332)。再次,不是你正在尋找的結果。

所以,我猜Suomen Verkkomaksut做了一些他們沒有記錄的prehash字符串的按摩,或者你忽略了。

+0

如果字節小於10,你的散列函數不填零。 – BalusC 2009-12-03 12:12:59

+0

好吧,也許我只需要等待他們的答案。感謝您提供的代碼示例。 – 2009-12-03 12:15:07

+0

@BususC。你太對了。我糾正了我的例子。總是打敗我爲什麼Java沒有Byte.toHexString和Byte.toUpperHexString來做正確的事情。 – 2009-12-03 12:35:05

1

如果您發送他們當作ISO-8859-1 UTF-8編碼的數據,那麼這可能是你的問題的根源。我建議你要麼發送ISO-8859-1的數據,要麼嘗試與Suomen Verkkomaksut通信,發送UTF-8。在基於http的協議中,您可以通過在HTTP標頭中將Content-Type添加charset = utf-8來實現此目的。

排除一些問題的一種方法是嘗試prehash字符串,該字符串只包含在UTF-8和ISO-8859-1中編碼相同的字符。從我所看到的你可以通過刪除你使用的字符串中的所有「ä」字符來實現這一點。

+0

我已經有和<?xml version =「1.0」encoding =「UTF-8」?> on這一頁。不幸的是,這些似乎沒有幫助。但你說得對,也許我應該聯繫他們。 – 2009-12-03 10:36:58

8

您似乎誤解了字符串編碼的工作方式,並且您的類的API值得懷疑。

字符串並不真正「有編碼」 - 編碼是用於在字符串和字節之間進行轉換的。

Java字符串在內部存儲爲UTF-16,但這並不重要,因爲MD5在字節而不是字符串上工作。您的Crypt.md5sum()方法必須先將它傳遞給字節的字符串進行轉換 - 它使用什麼編碼來實現?這可能是你的問題的根源。

你的示例代碼是這行的唯一效果相當無厘頭:

String prehashIso = new String(prehash.getBytes("ISO-8859-1"), "ISO-8859-1"); 

是更換不能在ISO-8859-1用問號表示的字符。

+0

感謝您的澄清。 – 2009-12-03 11:37:30

+0

+1對Crypt類的可疑性。它也表明加密和加密散列之間可能存在混淆(但根據課程的其他部分,也可能不會有一個)。 – Romain 2009-12-03 12:10:18

2

不知道你是否解決了你的問題,但是我對ISO-8859-1編碼的字符串有北歐字符和計算SHA-256哈希以與文檔中的東西進行比較有類似的問題。下面的代碼段爲我工作:

import java.security.MessageDigest; 
//imports omitted 

@Test 
public void test() throws ProcessingException{ 
String test = "iamastringwithäöchars";   
System.out.println(this.digest(test));  
} 

public String digest(String data) throws ProcessingException { 
    MessageDigest hash = null; 

    try{ 
     hash = MessageDigest.getInstance("SHA-256"); 
    } 
    catch(Throwable throwable){ 
     throw new ProcessingException(throwable); 
    } 
    byte[] digested = null; 
    try { 
     digested = hash.digest(data.getBytes("ISO-8859-1")); 
    } catch (UnsupportedEncodingException e) { 
     e.printStackTrace(); 
    } 

    String ret = BinaryUtils.BinToHexString(digested); 
    return ret; 
} 

要變換字節十六進制字符串有很多種選擇,包括在這個線程中提到的Apache的百科全書編解碼器六角類。