2009-12-03 121 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的百科全书编解码器六角类。