Невозможно создать информационный хеш торрента
Мне не удается найти проблему с тем, как я генерирую соответствующий информационный хэш для торрент-файла. Вот код, который у меня есть:
InputStream input = null;
try {
MessageDigest sha1 = MessageDigest.getInstance("SHA-1");
input = new FileInputStream(file);
StringBuilder builder = new StringBuilder();
while (!builder.toString().endsWith("4:info")) {
builder.append((char) input.read()); // It's ASCII anyway.
}
ByteArrayOutputStream output = new ByteArrayOutputStream();
for (int data; (data = input.read()) > -1; output.write(data));
sha1.update(output.toByteArray(), 0, output.size() - 1);
this.infoHash = sha1.digest();
System.out.println(new String(Hex.encodeHex(infoHash)));
} catch (NoSuchAlgorithmException | IOException e) {
e.printStackTrace();
} finally {
if (input != null) try { input.close(); } catch (IOException ignore) {}
}
Ниже мой ожидаемый и фактический хэш:
Expected: d4d44272ee5f5bf887a9c85ad09ae957bc55f89d
Actual: 4d753474429d817b80ff9e0c441ca660ec5d2450
Торрент, для которого я пытаюсь сгенерировать информационный хеш, можно найти здесь (Ubuntu 14.04 Desktop amd64).
Дайте мне знать, если я могу предоставить дополнительную информацию, спасибо!
2 ответа
Исключения содержат 4 полезных бита информации: тип, сообщение, трассировка и причина. Вы выбросили 3 из 4 соответствующих битов информации. Кроме того, код является частью процесса, и при возникновении ошибки этот процесс вообще не может быть завершен. И все же в исключительных случаях ваш процесс продолжается. Прекратите это делать; вы написали код, который вам только вредит. Удалите попытку и уловку. Добавитьthrows
в подписи вашего метода. Если вы не можете, переход по умолчанию (и обновите свою IDE, если он сгенерировал этот код для этого):throw new RuntimeException("Unhandled", e);
. Это короче, не уничтожает ни одного из 4 интересных фрагментов информации и завершает процесс.
Отдельно идея о том, что правильный способ обработки входящего потока close
метод IOException
бытие: Просто игнорируйте это, тоже ложно. Это маловероятно, но если это произойдет, вы должны предположить, что вы не прочитали каждый байт. Поскольку это было бы одним из объяснений несоответствия хеша, оно ошибочно.
Наконец, используйте правильные языковые конструкции: здесь есть инструкция try-with-resources, которая сработает намного лучше.
Вы звоните в обновление с output.size() - 1
; если вы не хотите намеренно игнорировать последний байт, это ошибка; вы обрезаете последний прочитанный байт.
Чтение байтов в построитель, а затем побайтовое преобразование построителя в строку с последующей проверкой последнего символа невероятно неэффективно; для файла размером всего 1 МБ это вызовет немало хлопот.
Чтение одного байта из необработанного FileInputStream
также этот уровень неэффективен, потому что каждое чтение вызывает доступ к файлу (чтение 1 байта так же дорого, как чтение всего буфера, поэтому оно примерно в 50000 раз медленнее, чем должно быть).
Вот как это сделать с помощью несколько более нового API, и посмотрите, насколько лучше читается этот код. Также он лучше действует при ошибочных условиях:
byte[] data = Files.readAllBytes(Paths.get(fileName));
var search = "4:info".getBytes(StandardCharsets.US_ASCII);
int searchIdx = -1;
for (int i = 0; searchIdx == -1 && i < data.length - search.length; i++) {
for (int j = 0; j < search.length; j++) {
if (data[i + j] != search[j]) break;
if (j == search.length - 1) searchIdx = i + j;
}
}
if (searchIdx == -1) throw new IOException("Input torrent file does not contain marker");
var sha1 = MessageDigest.getInstance("SHA-1");
sha1.update(data, searchIdx, data.length - searchIdx);
byte[] hash = sha1.digest();
StringBuilder hex = new StringBuilder();
for (byte h : hash) hex.append(String.format("%02x", h));
System.out.println(hex);
Хотя ответ rzwitserloot охватывает некоторые общие практики кодирования java, на уровне bittorrent также есть проблемы с правильностью.
Вы используете обработку строк для формата структурированных данных, это почти та же ошибка, что и попытка синтаксического анализа html с помощью регулярного выражения. В этом случае вы предполагаете, что единственное место, где данные могут содержать строку4:info
является ключом словаря верхнего уровня для информационного словаря, и что информационный словарь является последней записью словаря верхнего уровня.
Вместо этого вы должны использовать правильный декодер-кодировщик Bencoding для извлечения информационного словаря и затем перекодировать его для хеширования или токенизатора, чтобы найти точный диапазон байтов, покрывающий информационное значение. Обратите внимание, что вам нужен проверяющий синтаксический анализатор для первого, в то время как последний также может обрабатывать некоторые крайние случаи, не соответствующие спецификации. Если вы не хотите реализовывать их самостоятельно, вы можете найти библиотеку, которая сделает это за вас.
Кроме того, вы предполагаете, что это данные в формате ASCII. На самом деле bencoding - это двоичный формат, который в некоторых местах имеет тенденцию использовать ascii по соглашению. Вы должны работать с байтовыми массивами напрямую. Ваш ввод уже является двоичным, хешер ожидает двоичного, поэтому довольно обходным путем проходить строки.