MD5 хэш строки ISO-8859-1 в Java - PullRequest
3 голосов
/ 03 декабря 2009

Я реализую интерфейс для сервиса цифровых платежей под названием 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.Правильное значение для этого примера составляет 975816A41B9EB79B18B3B4526569640E в соответствии с документацией Суомен Verkkomaksut.

Есть ли способ вычисления хеша MD5 в Java со строками ISO-8859-1?

ОБНОВЛЕНИЕ: В ожидании ответа от Суомен Верккомаксут я нашел альтернативный способ создания хэша.Майкл Боргвардт исправил мое понимание String и кодировок, и я искал способ сделать хеш из байта [].

Apache Commons - отличный источник библиотек, и я нашел их класс DigestUtils, который имеет функцию md5hex, котораяпринимает байтовый ввод [] и возвращает шестнадцатеричную строку из 32 символов.

По какой-то причине это все еще не работает.Оба они возвращают одно и то же значение:

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

Ответы [ 4 ]

9 голосов
/ 03 декабря 2009

Вы, похоже, неправильно понимаете, как работает строковое кодирование, и API вашего Crypt класса является подозрительным.

Строки на самом деле не имеют "кодировки" - кодировка - это то, что вы используете для преобразования между строками и байтами.

Строки Java хранятся внутри как UTF-16, но это не имеет значения, поскольку MD5 работает с байтами, а не со строками. Ваш метод Crypt.md5sum() должен сначала преобразовать строки, которые он передает, в байты - какую кодировку он использует для этого? Это, вероятно, источник вашей проблемы.

Ваш пример кода довольно бессмысленный, так как единственный эффект этой строки:

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

- заменить символы, которые не могут быть представлены в ISO-8859-1, на вопросительные знаки.

2 голосов
/ 12 июля 2011

Не уверен, что вы решили свою проблему, но у меня была похожая проблема с зашифрованными строками 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;
}

Для преобразования байтов в шестнадцатеричную строку существует множество параметров, включая класс Hex кодека Apache Commons, упомянутый в этой теме.

2 голосов
/ 03 декабря 2009

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". Итак, я думаю, ваш класс Крипто реабилитирован. Я специально добавил распечатки длиной prehash и prehashBytes, чтобы убедиться, что действительно используется ISO-8859-1. В этом случае оба 328.

Когда я сделал presash.getBytes( "utf-8" ), он выдал «9CC2E0D1D41E67BE9C2AB4AABDB6FD3» (и длина байтового массива стала 332). Опять же, не результат, который вы ищете.

Итак, я полагаю, что Суомен Верккомаксут делает некоторый массаж строки prehash, которую они не задокументировали или вы пропустили.

1 голос
/ 03 декабря 2009

Если вы отправляете данные в кодировке UTF-8, которые они обрабатывают как ISO-8859-1, то это может стать источником вашей проблемы. Я предлагаю вам либо отправить данные в ISO-8859-1, либо попытаться сообщить Suomen Verkkomaksut, что вы отправляете UTF-8. В протоколе на основе http вы делаете это, добавляя charset = utf-8 в Content-Type в заголовке HTTP.

Чтобы исключить некоторые проблемы, попробуйте использовать строку предварительного хэширования, содержащую только те символы, которые закодированы одинаково в UTF-8 и ISO-8859-1. Из того, что я вижу, вы можете добиться этого, удалив все символы "ä" в строке, которую вы использовали.

...