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

Java中ISO-8859-1字符串的MD5哈希,java,utf-8,md5,iso-8859-1,Java,Utf 8,Md5,Iso 8859 1,我正在为数字支付服务实现一个接口,名为。有关付款的信息通过HTML表单发送给他们。为了确保在传输过程中没有人弄乱信息,在两端计算MD5散列,并使用未发送给他们的特殊密钥 我的问题是,出于某种原因,他们似乎认为传入的数据是用ISO-8859-1编码的,而不是UTF-8。我发送给他们的散列是用UTF-8字符串计算的,因此它不同于他们计算的散列 我尝试了以下代码: String prehash = "6pKF4jkv97zmqBJ3ZL8gUw5DfT2NMQ|13466|123456||Testit

我正在为数字支付服务实现一个接口,名为。有关付款的信息通过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纠正了我对字符串和编码的理解,我寻找了一种从字节[]生成哈希的方法

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

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

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

如果您发送他们视为ISO-8859-1的UTF-8编码数据,那么这可能是问题的根源。我建议您要么发送ISO-8859-1中的数据,要么尝试与Suomen Verkkomaksut沟通您正在发送UTF-8。在基于http的协议中,可以通过将charset=utf-8添加到http头中的内容类型来实现这一点


排除某些问题的一种方法是尝试使用预灰化字符串,该字符串仅包含UTF-8和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( ) );
包含java.security.MessageDigest;
//未显示异常处理
字符串预灰分=。。。
最终字节[]prehashBytes=prehash.getBytes(“iso-8859-1”);
System.out.println(prehash.length());
System.out.println(prehashBytes.length);
final MessageDigest digester=MessageDigest.getInstance(“MD5”);
消化器更新(字节);
最终字节[]digest=digester.digest();
final StringBuffer hexString=新StringBuffer();
for(最终字节b:摘要){
最终int intByte=0xFF&b;
if(int字节<10)
{
附加(“0”);
}
hexString.append(
整数.toHexString(intByte)
);
}
System.out.println(hexString.toString().toUpperCase());
不幸的是,它会产生相同的“C83CF67455AF10913D54252737F30E21”散列。所以,我猜你的加密类是无罪的。我特别添加了
prehash
prehashBytes
长度打印输出,以验证是否确实使用了“ISO-8859-1”。在这种情况下,两者都是328

当我执行
presash.getBytes(“utf-8”)
时,它生成了“9CC2E0D1D41E67BE9C2AB4AABDB6FD3”(字节数组的长度变为332)。再说一遍,这不是你想要的结果


所以,我猜Suomen Verkkomaksut对他们没有记录或您忽略的
prehash
字符串进行了一些处理。

不确定您是否解决了问题,但我遇到了一个类似的问题,即使用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 codec十六进制类。

我已经在页面上提供了和。不幸的是,这些似乎没有帮助。但是你是对的,也许我应该直接联系他们。+1关于
Crypt
类的可疑性。它还表明加密和加密哈希之间可能存在混淆(但也可能没有,这取决于类的其他部分)。如果字节小于10,则哈希函数不会填充零。啊,好吧,也许我只能等待他们的回答。感谢您提供的代码示例。@BalusC。你说得很对。我已经改正了我的例子。我总是不明白为什么Java没有Byte.toHexString和Byte.toUpperHexString来做正确的事情。我不得不重新构建大量的哈希,因为我使用了自己的、而且已经损坏的实现来进行字节[]到字符串的转换。如果要获得可预测的结果,请不要使用第一种形式的
getBytes()
。它使用系统的默认编码。您来自芬兰,您的默认编码很可能是“ISO-8859-1”,因此两个调用将产生相同的结果。