簡體   English   中英

無法創建 torrent 的信息哈希

[英]Unable to create a torrent's info hash

我無法找到如何為 Torrent 文件生成相應信息哈希的問題。 這是我到目前為止的代碼:

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

我正在嘗試為其生成信息哈希的 torrent 可以在這里找到(Ubuntu 14.04 Desktop amd64)

如果我能提供更多信息,請告訴我,謝謝!

異常包含 4 個有用的信息位:類型、消息、跟蹤和原因。 您已經丟棄了 4 個相關信息中的 3 個。 此外,代碼是進程的一部分,當發生錯誤時,通常該進程根本無法完成。 然而,在例外情況下,您的過程會繼續。 停止這樣做; 你寫的代碼只會傷害你。 刪除嘗試和捕獲。 在方法簽名上添加throws子句。 如果你不能,那么默認的(如果生成了這個代碼來更新你的 IDE)是throw new RuntimeException("Unhandled", e); . 這更短,不會破壞 4 個有趣的信息位中的任何一個,並結束一個過程。

另外,處理輸入流close方法的IOException的正確方法是:忽略它的概念也是錯誤的。 拋出的可能性很小,但如果拋出,您應該假設您沒有讀取每個字節。 由於這將是對不匹配哈希的一種解釋,因此被誤導了。

最后,使用正確的語言結構:這里有一個 try-with-resources 語句,效果會更好。

您正在使用output.size() - 1調用更新; 除非你想故意忽略最后一個字節,否則這是一個錯誤; 您正在刪除讀取的最后一個字節。

將字節讀入構建器,然后按字節將構建器轉換為字符串,然后檢查最后一個字符的效率非常低; 對於小到 1MB 的文件,這會造成相當大的麻煩。

從原始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 級別上也存在正確性問題。

您正在對結構化數據格式使用字符串處理,這與嘗試使用 regex 解析 html 的錯誤幾乎相同。 在這種情況下,您假設數據可以包含字符串4:info的唯一位置是 info dict 的頂級字典鍵,並且 info 字典是頂級字典的最后一個條目。

相反,您應該使用適當的編碼解碼器-編碼器來提取信息字典,然后重新編碼它以進行散列或標記器以找到覆蓋信息值的確切字節范圍。 請注意,前者需要一個驗證解析器,而后者也可以處理一些超出規范的邊緣情況。 除非你想自己實現它們,否則你可能想找到一個為你處理這個的庫。

此外,您假設數據是 ASCII。 bencoding 實際上是一種二進制格式,在某些地方習慣於使用 ascii。 您應該直接對字節數組進行操作。 您的輸入已經是二進制的,散列器需要二進制,因此遍歷字符串非常迂回。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM