将二进制SHA-1摘要转换为十六进制字符串的奇怪算法

3
在互联网上我找到了以下代码可以生成SHA1哈希值:
        public static String hash(String str) {
            try {
                    MessageDigest mg = MessageDigest.getInstance("SHA-1");
                    byte[] result = mg.digest(str.getBytes());
                    StringBuffer sb = new StringBuffer();
                    for (int i = 0; i < result.length; i++) {
                            sb.append(Integer.toString((result[i] & 0xff) + 0x100, 16).substring(1));
                    }
                    return sb.toString();
            } catch (NoSuchAlgorithmException e) {
                    System.err.println("SHA-1 not found.");
                    return "";
            }
    }

但是为什么要加上(result[i] & 0xff) + 0x100呢?


请在发布前阅读常见问题解答。 - Nikhil Agrawal
@Nikhil:这篇文章有什么问题吗? - Pino
@pino复制代码并在这里问。他不能先参考官方文件吗?并展示他的研究努力。 - Nikhil Agrawal
@Nikhil:我不同意。有哪个官方文件可以回答他的问题呢?复制别人的代码并不是FAQ所反对的,相反,FAQ说:“我们认为最好的Stack Overflow问题中包含一些源代码”。他遇到了一个自己无法理解的软件算法问题:这完全是主题范围内的。 - Pino
2个回答

6
字节是有符号的:它们可以是负数。当处理负字节时,Integer.toString() 生成以 "FFFFFF" 开头的字符串,但对于正字节,则不会发生这种情况,因此结果字符串的长度不固定。 & 0xff 将字节转换为无符号整数。然后添加 0x100 以确保十六进制字符串为 3 个字符长;这是必需的,因为我们希望每个字节有 2 个十六进制数字的字符串,但介于 0 和 15 之间的字节将仅产生 1 个字符。最后,第三个数字将使用 substring(1) 舍去。
我建议将 StringBuffer 替换为 StringBuilder,因为它略微更高效,并且指定初始缓冲区长度:
StringBuilder sb = new StringBuilder(result.length * 2);

1

如果byte被提升为大于8位的类型并进行符号扩展,则& 0xff将起作用。

在这里,符号扩展是一个非常真实的问题,因此至少需要0xff来解决这个问题。


“0xFF”确实是必需的,正如您所得出的结论一样。我建议您重新表述答案,因为它现在有点令人困惑。 - Mark Rotteveel
“万一字节超过了8位”听起来非常糟糕。 - Pino
真的。我从不喜欢对底层硬件做出假设,但在这种情况下,Java规范似乎表明一个字节永远不能超过8位,因此我简化了我的答案。 - Edward Falk

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接