4

我最近开始研究 MD5 散列(在 Java 中),虽然我找到了帮助我实现这一点的算法和方法,但我想知道它实际上是如何工作的。

一方面,我从这个 URL中找到了以下内容:

private static String convertToHex(byte[] data) {
    StringBuffer buf = new StringBuffer();
    for (int i = 0; i < data.length; i++) {
        int halfbyte = (data[i] >>> 4) & 0x0F;
        int two_halfs = 0;
        do {
            if ((0 <= halfbyte) && (halfbyte <= 9))
                buf.append((char) ('0' + halfbyte));
            else
                buf.append((char) ('a' + (halfbyte - 10)));
                halfbyte = data[i] & 0x0F;
            } while(two_halfs++ < 1);
        }
    return buf.toString();
}

我还没有发现任何需要在 Java 中使用位移,所以我对此有点生疏。有人好心地说明(简单来说)上面的代码是如何进行转换的?>>>>”?

我还在 StackOverflow 上找到了其他解决方案,例如herehere,它们使用 BigInteger 代替:

try {
   String s = "TEST STRING";
   MessageDigest md5 = MessageDigest.getInstance("MD5");
   md5.update(s.getBytes(),0,s.length());
   String signature = new BigInteger(1,md5.digest()).toString(16);
   System.out.println("Signature: "+signature);

} catch (final NoSuchAlgorithmException e) {
   e.printStackTrace();
}

为什么这也有效,哪种方式更有效?

谢谢你的时间。

4

4 回答 4

10
private static String convertToHex(byte[] data) {
    StringBuffer buf = new StringBuffer();
    for (int i = 0; i < data.length; i++) {

到目前为止......只是基本设置并开始循环遍历数组中的所有字节

        int halfbyte = (data[i] >>> 4) & 0x0F;

转换为十六进制时的字节是两个十六进制数字或 8 个二进制数字,具体取决于您查看它的基数。上面的语句将高 4 位向下移动(>>> 是无符号右移)并将其与 0000 1111 进行逻辑与运算,以便结果是一个整数,等于字节的高 4 位(第一个十六进制数字)。

假设 23 是一个输入,这是二进制的 0001 0111。移位使逻辑与将其转换为 0000 0001。

        int two_halfs = 0;
        do {

这只是将 do/while 循环设置为运行两次

            if ((0 <= halfbyte) && (halfbyte <= 9))
                buf.append((char) ('0' + halfbyte));
            else
                buf.append((char) ('a' + (halfbyte - 10)));

在这里,我们显示实际的十六进制数字,基本上只是使用零或字符作为起点并向上移动到正确的字符。第一个 if 语句涵盖所有数字 0-9,第二个涵盖所有数字 10-15(十六进制中的 af)

同样,使用我们的示例,十进制的 0000 0001 等于 1。我们被困在上面的 if 块中,并将 1 加到 '0' 字符以获得字符 '1',将其附加到字符串并继续前进。

                halfbyte = data[i] & 0x0F;

现在我们将整数设置为刚好等于字节中的低位并重复。

同样,如果我们的输入是 23 ... 0001 0111 之后逻辑与变为 0000 0111 即十进制的 7。重复上述相同的逻辑并显示字符“7”。

            } while(two_halfs++ < 1);

现在我们只需移动到数组中的下一个字节并重复。

        }
    return buf.toString();
}

为了回答您的下一个问题,Java API 已经在 BigInteger 中内置了一个基本转换实用程序。请参阅toString(int radix)文档。

不知道 Java API 使用的实现,我不能肯定地说,但我愿意打赌 Java 实现比您发布的第一个稍微简单的算法更有效。

于 2009-06-25T11:23:25.253 回答
2

要回答这个问题:

为什么这也有效

它没有。至少,与循环版本的方式不同。new BigInteger(...).toString(16) 不会显示前导零,前一个版本会。通常对于像写出一个字节数组(尤其是一个表示像哈希这样的东西)这样的东西,你会想要一个固定长度的输出,所以如果你想使用那个版本,你必须适当地填充它。

于 2009-06-26T01:24:29.483 回答
1

有关位移的详细解释,请查看以下 SO 问题中的答案 什么是位移(位移)运算符以及它们如何工作?

他似乎试图将单个字节转换为小于 16 的数字,通过这样做,他可以轻松地确定该字节用代码表示的字符

  if ((0 <= halfbyte) && (halfbyte <= 9))
                buf.append((char) ('0' + halfbyte));
            else
                buf.append((char) ('a' + (halfbyte - 10)));

这是一个简单的答案,但无论如何我并不那么聪明=D

于 2009-06-25T11:19:36.337 回答
0

这些东西你不用自己写,因为已经用apache-commons-codec写好了:

import org.apache.commons.codec.binary.Hex;
...
Hex.encodeHexString(byte[] array)

课堂上有很多更有用的方法Hex

于 2014-04-28T13:54:19.727 回答