Java 中 ISO-8859-1 字符串的 MD5 哈希值

发布于 2024-08-12 21:32:36 字数 1573 浏览 10 评论 0原文

我正在实现一个名为 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|[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 纠正了我对字符串和编码的理解,我寻找一种从 byte[] 生成哈希的方法。

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

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

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

I'm implementing an interface for digital payment service called Suomen Verkkomaksut. The information about the payment is sent to them via HTML form. To ensure that no one messes with the information during the transfer a MD5 hash is calculated at both ends with a special key that is not sent to them.

My problem is that for some reason they seem to decide that the incoming data is encoded with ISO-8859-1 and not UTF-8. The hash that I sent to them is calculated with UTF-8 strings so it differs from the hash that they calculate.

I tried this with following code:

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

Unfortunately both hashes are identical with value C83CF67455AF10913D54252737F30E21. The correct value for this example case is 975816A41B9EB79B18B3B4526569640E according to Suomen Verkkomaksut's documentation.

Is there a way to calculate MD5 hash in Java with ISO-8859-1 strings?

UPDATE: While waiting answer from Suomen Verkkomaksut, I found an alternative way to make the hash. Michael Borgwardt corrected my understanding of String and encodings and I looked for a way to make the hash from byte[].

Apache Commons is an excellent source of libraries and I found their DigestUtils class which has a md5hex function which takes byte[] input and returns a 32 character hex string.

For some reason this still doesn't work. Both of these return the same value:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

玉环 2024-08-19 21:32:36

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

字符串实际上并不“有编码” - 编码是您用来在字符串和字节之间进行转换的内容。

Java 字符串在内部存储为 UTF-16,但这并不重要,因为 MD5 适用于字节,而不是字符串。您的 Crypt.md5sum() 方法必须首先将其传递的字符串转换为字节 - 它使用什么编码来执行此操作?这可能就是你的问题的根源。

您的示例代码非常无意义,因为该行的唯一效果是:

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

用问号替换无法在 ISO-8859-1 中表示的字符。

You seem to misunderstand how string encoding works, and your Crypt class's API is suspect.

Strings don't really "have an encoding" - an encoding is what you use to convert between Strings and bytes.

Java Strings are internally stored as UTF-16, but that does not really matter, as MD5 works on bytes, not Strings. Your Crypt.md5sum() method has to convert the Strings it's passed to bytes first - what encoding does it use to do that? That's probably the source of your problem.

Your example code is pretty nonsensical as the only effect this line has:

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

is to replace characters that cannot be represented in ISO-8859-1 with question marks.

乖乖 2024-08-19 21:32:36

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

Java has a standard java.security.MessageDigest class, for calculating different hashes.

Here is the sample code

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

Unfortunately for you it produces the same "C83CF67455AF10913D54252737F30E21" hash. So, I guess your Crypto class is exonerated. I specifically added the prehash and prehashBytes length printouts to verify that indeed 'ISO-8859-1' is used. In this case both are 328.

When I did presash.getBytes( "utf-8" ) it produced "9CC2E0D1D41E67BE9C2AB4AABDB6FD3" (and the length of the byte array became 332). Again, not the result you are looking for.

So, I guess Suomen Verkkomaksut does some massaging of the prehash string that they did not document, or you have overlooked.

老子叫无熙 2024-08-19 21:32:36

不确定你是否解决了你的问题,但我对带有 Nordic ä & 的 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 类。

Not sure if you solved your problem, but I had a similar problem with ISO-8859-1 encoded strings with nordic ä & ö characters and calculating a SHA-256 hash to compare with stuff in documentation. The following snippet worked for me:

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

To transform bytes to hex string there are many options, including the apache commons codec Hex class mentioned in this thread.

说不完的你爱 2024-08-19 21:32:36

如果您发送 UTF-8 编码的数据,他们将其视为 ISO-8859-1,那么这可能是问题的根源。我建议您以 ISO-8859-1 格式发送数据,或者尝试与 Suomen Verkkomaksut 沟通您正在发送 UTF-8。在基于 http 的协议中,您可以通过将 charset=utf-8 添加到 HTTP 标头中的 Content-Type 来实现此目的。

排除某些问题的一种方法是尝试使用仅包含以 UTF-8 和 ISO-8859-1 编码的相同字符的预哈希字符串。据我所知,您可以通过删除所使用的字符串中的所有“ä”字符来实现此目的。

If you send UTF-8 encoded data that they treat as ISO-8859-1 then that could be the source of your problem. I suggest you either send the data in ISO-8859-1 or try to communicate to Suomen Verkkomaksut that you're sending UTF-8. In a http-based protocol you do this by adding charset=utf-8 to Content-Type in the HTTP header.

A way to rule out some issues would be to try a prehash String that only contains characters that are encoded the same in UTF-8 and ISO-8859-1. From what I can see you can achieve this by removing all "ä" characters in the string you'e used.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文