【问题标题】:MD5 Hash of ISO-8859-1 string in JavaJava 中 ISO-8859-1 字符串的 MD5 哈希
【发布时间】:2009-12-03 10:21:31
【问题描述】:

我正在实现一个名为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。

有没有办法在 Java 中使用 ISO-8859-1 字符串计算 MD5 哈希?

更新:在等待 Suomen Verkkomaksut 的答复时,我找到了另一种制作哈希的方法。 Michael Borgwardt 纠正了我对字符串和编码的理解,我寻找一种方法从 byte[] 生成哈希。

Apache Commons 是一个很好的库来源,我发现他们的 DigestUtils 类有一个 md5hex 函数,它接受 byte[] 输入并返回一个 32 个字符的十六进制字符串。

由于某种原因,这仍然不起作用。这两个都返回相同的值:

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

【问题讨论】:

  • 如果您想获得可预测的结果,切勿使用getBytes() 的第一种形式。它使用系统的默认编码。您来自芬兰,您的默认编码很可能是“ISO-8859-1”,因此两次调用都会产生相同的结果。

标签: java utf-8 md5 iso-8859-1


【解决方案1】:

您似乎误解了字符串编码的工作原理,并且您的 Crypt 类的 API 值得怀疑。

字符串实际上并没有“编码”——编码是你用来在字符串和字节之间进行转换的东西。

Java 字符串在内部存储为 UTF-16,但这并不重要,因为 MD5 处理的是字节,而不是字符串。您的 Crypt.md5sum() 方法必须首先将它传递的字符串转换为字节 - 它使用什么编码来做到这一点?这可能是您问题的根源。

您的示例代码非常荒谬,因为这一行的唯一效果是:

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

就是用问号代替ISO-8859-1中不能表示的字符。

【讨论】:

  • +1 关于Crypt 类的可疑性。它还表明加密和加密哈希之间可能存在混淆(但也可能没有,这取决于课程的其余部分)。
【解决方案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 字符串进行了一些按摩,或者您忽略了。

【讨论】:

  • 如果字节小于 10,您的哈希函数不会填充零。
  • 嗯,也许我只需要等待他们的答复。感谢您提供的代码示例。
  • @BalusC。你说的很对。我已经纠正了我的例子。总是打败我为什么 Java 没有 Byte.toHexString 和 Byte.toUpperHexString 做正确的事情。
  • 只需使用 Hex 类的 apache commons 编解码器即可。我不得不重建大量的哈希值,因为我使用了我自己的、损坏的实现来实现 byte[] 到 String 的转换。
【解决方案3】:

不确定您是否解决了问题,但我遇到了类似的问题,即 ISO-8859-1 编码字符串与北欧 ä 和 ö 字符并计算 SHA-256 哈希以与文档中的内容进行比较。以下 sn-p 对我有用:

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 codec Hex 类。

【讨论】:

    【解决方案4】:
    猜你喜欢
    • 2013-01-19
    • 2023-04-08
    • 2020-01-25
    • 1970-01-01
    • 1970-01-01
    • 2012-10-10
    • 1970-01-01
    • 2012-07-12
    • 2010-12-02
    相关资源
    最近更新 更多