将Java哈希转换为随机字符串

6
我正在为彩虹表生成器开发一个降维函数。
降维函数的基本原理是,它接受哈希值,执行一些计算,并返回长度为特定值的字符串。
目前我正在使用SHA1哈希值,并且需要返回一个长度为3的字符串。我需要这个字符串由以下任意三个随机字符组成:
abcdefghijklmnopqrstuvwxyz0123456789

我面临的主要问题是,我写的任何缩减函数都会返回已经生成的字符串。而一个好的缩减函数很少会返回重复的字符串。

有人能提出任何关于如何完成这个任务的想法吗?或者对哈希到字符串的操作有任何建议都可以。

提前感谢

Josh

2个回答

6
看起来你需要将长度为20位的SHA1哈希值映射到三位36进制数字。我建议使用Java中的BigInteger库,将哈希字节转换为大整数后,对36^3取模,最后返回36进制字符串即可。请参考BigInteger文档
public static final BigInteger N36POW3 = new BigInteger(""+36*36*36));
public static String threeDigitBase36(byte[] bs) {
  return new BigInteger(bs).mod(N36POW3).toString(36);
}
// ...
threeDigitBase36(sha1("foo")); // => "96b"
threeDigitBase36(sha1("bar")); // => "y4t"
threeDigitBase36(sha1("bas")); // => "p55"
threeDigitBase36(sha1("zip")); // => "ej8"

当然会出现冲突,就像将任何空间映射到更小的空间一样,但熵应该比上述解决方案更好。

我的错,我会删除那个答案。这是与同余关系中存在逆元素相关的属性(在这种情况下不需要)。 - Gabriel Belingueres
非常感谢,这两个答案都很棒,但我选择了Bohemian的答案,因为它稍微短一些。再次感谢所有的评论;StackOverflow上的人们再次证明自己无论做什么都不够得到报酬!!! - Joshua Craven
1
@JoshuaCraven:非常感谢!请注意,我的答案和Bohemian的答案可能会返回长度为1、2或3的字符串,因此如果长度小于3,则需要用零填充返回的字符串。 - maerics

4

应用KISS原则:

  • SHA只是一个字符串
  • String的JDK哈希码足够随机
  • Integer可以呈现任何基数

这一行代码就可以了:

public static String shortHash(String sha) {
    return Integer.toString(sha.hashCode() & 0x7FFFFFFF, 36).substring(0, 3);
}

注意: & 0x7FFFFFFF 的作用是将符号位清零(哈希码可以是负数,否则会以负号开头显示)。

编辑 - 保证哈希长度

我的原始解决方案很幼稚——它没有处理int哈希小于100(36进制)的情况,这意味着它将打印少于3个字符。这段代码修复了这个问题,同时仍然保持“随机”值。它还避免了substring()调用,因此性能应该更好。
static int min = Integer.parseInt("100", 36);
static int range = Integer.parseInt("zzz", 36) - min;

public static String shortHash(String sha) {
    return Integer.toString(min + (sha.hashCode() & 0x7FFFFFFF) % range, 36);
}

这段代码通过强制哈希值在 100zzz 之间(即在基数为 36 时的最低和最高三字符哈希)生成,以确保最终哈希值具有 3 个字符,并仍然保持“随机”。

很抱歉重新提出已经回答过的问题。但是,如果我将您的答案中子字符串的范围从(0,3)更改为(0,4),则生成的长度小于4个字符的子字符串数量会成为一个相当大的问题。此外,该函数似乎运行非常缓慢。您能解释一下原因吗? - Joshua Craven
@JoshuaCraven 好的 - 我添加了更多的代码来解决你的问题。顺便说一下,你提出了很好的观点。 - Bohemian
顶级的东西。非常感谢你的帮助!! - Tony

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