2

我注意到我的很多谷歌搜索都把我带到了这里,所以我想也许我可以借用你的聪明才智:)

作为我三年级学位论文的一部分,我正在为移动设备(以及用于登录的网站)开发一次性密码生成器。

使用 org.bouncycastle.crypto.digests.MD5Digest 库,我正在获取一个字节数组(来自字符串用户输入),然后将其散列 X 次。这也称为菊花链散列字符串或加密方法。

我的问题是,如果字符串被散列一次,那么它正确地散列它,但是如果新的散列再次被散列,结果是不正确的。

请参见下面的代码:

private String generateHash(String OTP, int loopNum)
{
      byte[] secretBytes = OTP.getBytes();

      for (int x = 0; x < loopNum; x++)
      {
          byte[] tempStore = new byte[16];
          tempStore = hash(secretBytes);
          secretBytes = tempStore;
      }

      return convertToHex(secretBytes);
}

public byte[] hash(byte[] secretBytes)
{
        org.bouncycastle.crypto.digests.MD5Digest digest = new org.bouncycastle.crypto.digests.MD5Digest();

        digest.reset();

        // Update MD5 digest with user secret in byte format
        digest.update(secretBytes, 0, secretBytes.length);

        // get length of digest to initialise new md5 byte array
        int length = digest.getDigestSize();

        // create md5 byte array using length
        byte[] md5 = new byte[length];

        // calculate MD5 hash, using md5 byte array, 0 for buffer offset
        digest.doFinal(md5, 0);

        return md5;
}


private static String convertToHex(byte[] data) {
        StringBuffer buf = new StringBuffer();
        String Hex;
        String formattedHex;
        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);
        }

        Hex = buf.toString();

        formattedHex = "\n"  + Hex.substring(0, 4) +  " " + Hex.substring(4, 8) + " " + Hex.substring(8, 12) + " "
               + Hex.substring(12, 16) +  " " + Hex.substring(16, 20) +  " "  +Hex.substring(20, 24) + " "
               + Hex.substring(24, 28) +  " " + Hex.substring(28, 32);
        return formattedHex;
    }

我认为两者都是;

  1. 摘要未返回正确的字节数组
  2. 十六进制转换器错误地转换了这个

我使用以下秘密进行测试:A 具有以下 MD5 输出:

  1. 7fc56270e7a70fa81a5935b72eacbe29
  2. 8f28f2e7231860115d2a8cacba019dbe(这应该是 4cbd6d53280de25e04712c7434a70642)

非常感谢您提前提供的帮助:)

ps 我正在对照 PHP md5 检查它,这也可能是一个问题吗?

4

2 回答 2

5

MD5,当应用于由值 0x41(一个'A')的单个字节组成的输入时,会产生一个 16 字节的输出,当以十六进制打印时,它是7fc56270e7a70fa81a5935b72eacbe29.

如果你对这16个字节应用 MD5,你应该得到8f28f2e7231860115d2a8cacba019dbe,这就是你得到的。

现在,如果您考虑将 MD5 应用于32 字节字符串,它是字符串“ 7fc56270e7a70fa81a5935b72eacbe29”的 ASCII 编码,那么这将产生4cbd6d53280de25e04712c7434a70642. 所以我认为你的 Java 代码很好(为此),你的困惑来自你如何将输入数据提供给基于 PHP 的测试代码。您编写7fc562...并认为它是“一个字节的值 0x7f,然后是一个字节的值 0xc5,然后......”但是 PHP 代码将其视为“一个字节的值 0x37('7' 的 ASCII 代码),然后是值 0x66 的一个字节('f' 的 ASCII 码),然后..."。

在 Linux 系统上,试试这个:

$ printf A | md5sum
7fc56270e7a70fa81a5935b72eacbe29  -
$ printf 7fc56270e7a70fa81a5935b72eacbe29 | md5sum
4cbd6d53280de25e04712c7434a70642  -
$ printf "\x7f\xc5\x62\x70\xe7\xa7\x0f\xa8\x1a\x59\x35\xb7\x2e\xac\xbe\x29" | md5sum
8f28f2e7231860115d2a8cacba019dbe  -

作为旁注:

  • 警惕OTP.getBytes()。它使用与语言环境相关的字符集将字符串转换为字节。这将使用 UTF-8、UTF-16、ISO-8859-1、...取决于系统配置,通常与“系统语言”相关联。您的代码在同一个字符串上的行为会有所不同,这很少是一个好主意。相反,OTP.getBytes("UTF-8")无论本地配置如何,使用 which 将计算相同的字节。
  • 您的哈希循环包含无用的咒语。例如,您分配了一个从未使用过的 16 字节数组。
  • 在 Java 中,变量名以大写字母开头被认为是不好的编码风格。如果您打算在学校环境中显示您的代码,那么您应该重命名Hexhex.
  • halfByte作为“ & 0x0F”的结果获得时,它必然包含一个介于 0 和 15 之间的值。“ 0 <= halfByte”测试是不必要的。
于 2010-02-03T21:13:39.877 回答
1

非常感谢大家的帮助,问题是我的 java md5 的行为不像 php MD5。

我找到了一个解决问题的方法,它基本上(在 Java 中)采用字节数组并将其转换为十六进制字符串,然后获取该字符串的字节,然后使用 MD5 而不是使用非十六进制字节数组。请参阅下面的解决方案

有关结果,请参见以下内容: http://forums.sun.com/thread.jspa?forumID=9& threadID=718781

static String byteArrayToHexString(byte byteValues[]) {
        byte singleChar = 0;
        if (byteValues == null || byteValues.length <= 0)
            return null;

        String entries[] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9",
                "a", "b", "c", "d", "e", "f" };

        StringBuffer out = new StringBuffer(byteValues.length * 2);

        for (int i = 0; i < byteValues.length; i++) {
            singleChar = (byte) (byteValues[i] & 0xF0);
            singleChar = (byte) (singleChar >>> 4);
            // shift the bits down
            singleChar = (byte) (singleChar & 0x0F);
            out.append(entries[(int) singleChar]); 
            singleChar = (byte) (byteValues[i] & 0x0F); 
            out.append(entries[(int) singleChar]);
        }
        String rslt = new String(out);
        return rslt;
    }

非常感谢所有发帖的人,感激不尽!

于 2010-02-03T23:22:45.327 回答