无法创建种子的信息哈希

问题描述 投票:0回答:2

我无法找到有关如何为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

我正在尝试为其生成信息散列的种子可以找到here (Ubuntu 14.04 Desktop amd64)

让我知道是否可以提供更多信息,谢谢!

java hash bittorrent torrent info-hash
2个回答
1
投票

异常包含4个有用的信息位:类型,消息,跟踪和原因。您已经丢掉了4个相关信息中的3个。而且,代码是流程的一部分,并且当发生错误时,通常该流程根本无法完成。但是在例外情况下,您的过程仍在继续。别这样您编写的代码只会伤害您。删除尝试,并抓住。在方法签名上添加throws子句。如果不能,则默认设置为throw new RuntimeException("Unhandled", e);(如果生成了此代码,则更新您的IDE)。这更短,不会破坏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);

0
投票

虽然rzwitserloot's answer涵盖了一些通用的Java编码实践,但在torrent级别上也存在正确性问题。

您正在对结构化数据格式使用字符串处理,这与尝试parse html with regex几乎是相同的错误。在这种情况下,您假设数据可以包含字符串4:info的唯一位置是info dict的顶级字典键,而info字典是顶级字典的最后一个条目。

相反,应该使用适当的bencoding解码器-编码器to extract the info dict and then re-encode it for hashing或标记器来找到覆盖info值的确切字节范围。请注意,您need a validating parser为前者,而后者也可以处理某些超出规格的情况。除非您想自己实现它们,否则可能希望找到一个可以为您处理的库。

另外,您假设数据为ASCII。实际上,bencoding是a binary format,在某些地方,按惯例习惯使用ascii。您应该直接对字节数组进行操作。您的输入已经是二进制,hasher期望是二进制,因此遍历字符串非常circuit回。

© www.soinside.com 2019 - 2024. All rights reserved.