我注意到我的很多 Google 搜索都把我带到了这里,所以我想也许我可以借用你的智慧 :)
作为我三年级学位论文的一部分,我正在为移动设备(以及用于登录的网站)开发一次性密码生成器。
使用 org.bouncycaSTLe.crypto.digests.MD5Digest 库,我获取一个字节数组(来自字符串用户输入),然后对其进行散列 X 次。这也称为菊花链哈希字符串或 lamports 加密方法。
我的问题是,如果字符串被散列一次,那么它会正确散列它,但是如果新的散列再次被散列,结果是不正确的。
见下面的代码:
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;
}
我认为是;
- 摘要没有返回正确的字节数组
- 十六进制转换器错误地转换了它
我正在使用以下 secret 进行测试:A 具有以下 MD5 输出:
- 7fc56270e7a70fa81a5935b72eacbe29
- 8f28f2e7231860115d2a8cacba019dbe(这应该是 4cbd6d53280de25e04712c7434a70642)
非常感谢您的提前帮助:)
附注我正在根据 PHP md5 检查它这是否也是一个问题?
最佳答案
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")
无论本地配置如何,它将计算相同的字节。 - 您的哈希循环包含无用的咒语。例如,您分配了一个 16 字节的数组,但您从未使用过它。
- 在 Java 中,变量名以大写字母开头被认为是糟糕的编码风格。如果您计划在学校环境中展示您的代码,那么您应该重命名
Hex
进入hex
. - 何时
halfByte
作为“& 0x0F
”的结果获得,那么它必然包含 0 到 15 之间的值。“0 <= halfByte
”测试是不必要的。
关于使用 bouncy caSTLe 的 Java ME MD5 字符串 - 不能多次散列,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2194222/