簡體   English   中英

MD5在Java中使用ISO-8859-1字符串哈希

[英]MD5 Hash of ISO-8859-1 string in Java

我正在實施名為Suomen Verkkomaksut的數字支付服務界面。 有關付款的信息將通過HTML表單發送給他們。 為了確保在傳輸過程中沒有人對信息感到困惑,MD5哈希在兩端用一個未發送給他們的特殊密鑰計算。

我的問題是,由於某種原因,他們似乎決定傳入的數據是用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|esimerkki@esimerkki.fi|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糾正了我對字符串和編碼的理解,並且我找到了一種從byte []創建哈希的方法。

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

由於某種原因,這仍然無效。 這兩個都返回相同的值:

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

您似乎誤解了字符串編碼的工作原理,並且您的Crypt類的API是可疑的。

字符串實際上並沒有“編碼” - 編碼就是用來在字符串和字節之間進行轉換的。

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

您的示例代碼非常荒謬,因為此行具有唯一的效果:

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

是用問號替換ISO-8859-1中無法表示的字符。

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字符串做了一些按摩,或者你忽略了。

不確定你是否解決了你的問題,但我對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 commons編解碼器Hex類。

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

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

暫無
暫無

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

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