我想知道Java中MD5和SHA-2 512 (SHA512)或256哈希算法的最佳且最快实现方式。我需要一个接受字符串参数并将哈希作为结果返回的函数。谢谢。
编辑:这是为了将每个URL映射到唯一的哈希值。由于MD5在这个领域不太可靠,所以我更感兴趣的是找到SHA-2算法的最佳和最快实现方式。请注意,我知道即使是SHA-2也可能对于某些URL产生相同的哈希值,但我可以接受这种情况。
String
在内部是由代表Unicode代码点的char
值数组中的一小块(实际上是使用UTF-16编码的Unicode 16位代码单元)组成的。哈希函数以位或字节序列作为输入。因此,您需要进行转换步骤,例如str.getBytes("UTF-8")
,以将字符串转换为一堆字节。与哈希本身相比,转换步骤可能具有不可忽略的成本。%
'符号开头的序列;这旨在支持不可打印字符,但也可以用于"标准"字符(例如,用'%61
'替换'a
')。这意味着在String.equals()
意义下不同的两个字符串实际上可能表示相同的URL(就URL处理而言)。根据您的情况,这可能是一个问题或不是一个问题。MessageDigest
API和标准(已安装)JCE提供程序(即,调用MessageDigest.getInstance("SHA-256")
),并测试结果。理论上,JCE可能将调用映射到具有“本地”代码(用C或汇编语言编写)的实现,这将比您可以使用Java获得的更快。修改:我最初看到这个问题时认为是什么是"最快的哈希算法",现在已经澄清为"每种算法的最快实现方式"。这是一个有效的问题,其他人已经指出了更快的实现方式。然而,除非你需要在短时间内散列大量数据,否则速度并不会对结果产生太大影响。我怀疑使用除了标准JCE提供的内容之外的东西可能不值得时间和复杂度。
对于URL地址,您需要在现代硬件上以SHA-256为基础进行散列,每秒高达一百万次才需要更快的算法。我不能想象大多数应用程序需要超过一千个每秒(每天超过8600万个),这意味着总CPU时间花费在散列上的比例将远远低于1%。因此,即使您拥有无限快的哈希算法,您也只能将整体性能提高1%。
原始回答:获取最好和最快的两者之间存在矛盾。更好的哈希通常更慢。如果您真的需要速度,并且安全性不是那么重要,那么使用MD5。如果您需要最佳安全性,则选择SHA-256甚至SHA-512。您没有提到您要使用它做什么,因此很难推荐哪种方法。在现代硬件上,使用SHA-256应该足够快,因此您可能最安全地选择它。以下是如何实现的:
String input = "your string";
MessageDigest digest = MessageDigest.getInstance("SHA-256");
digest.update(input.getBytes("UTF-8"));
byte[] hash = digest.digest();
如果您将此用于安全目的,例如对密码进行哈希,则还应向摘要中添加盐。如果您想从哈希中获得可打印的字符串,则可以将其编码为十六进制字符串:
static char[] HEX_CHARS = "0123456789ABCDEF".toCharArray();
StringBuilder sb = new StringBuilder(hash.length * 2);
for (byte b : hash) {
sb.append(HEX_CHARS[(b & 0xF0) >> 4]);
sb.append(HEX_CHARS[b & 0x0F]);
}
String hex = sb.toString();
toHexString()
的实现可以通过将十六进制数字的String
替换为char[]
并使用HEX_DIGITS[(b & 0xF0) >> 4]
而不是调用charAt()
来稍微改进。在(可能有缺陷的)微基准测试中,这被证明比原来快了约30%。 - Joachim Sauer请查看以下内容:许多SHA / MD5示例
另外:同一线程:快速MD5
使用以下代码可以生成文件的MD5哈希值:String hash = MD5.asHex(MD5.getHash(new File(filename)));
另一个要考虑的事情是使用MD4。它不像MD5那样安全,但计算速度更快。Windows直到XP时代都使用MD4来存储和交换密码,所以我们使用此哈希算法,因为它仍然可以为该平台提供身份验证服务。
https://www.cryptopp.com/benchmarks.html
在第一个链接中,BLAKE2b(947 Mbits)比SHA-256(413 Mbits)和MD5(632 Mbits)快得多。hashCode()
,因为在内存开销上更便宜。public static int hash8(String val) throws UnsupportedEncodingException {
return hash8(val.getBytes("UTF-8"));
}
public static int hash8(byte[] val) {
int h = 1, i = 0;
for (; i + 7 < val.length; i += 8) {
h = 31 * 31 * 31 * 31 * 31 * 31 * 31 * 31 * h + 31 * 31 * 31 * 31
* 31 * 31 * 31 * val[i] + 31 * 31 * 31 * 31 * 31 * 31
* val[i + 1] + 31 * 31 * 31 * 31 * 31 * val[i + 2] + 31
* 31 * 31 * 31 * val[i + 3] + 31 * 31 * 31 * val[i + 4]
+ 31 * 31 * val[i + 5] + 31 * val[i + 6] + val[i + 7];
}
for (; i + 3 < val.length; i += 4) {
h = 31 * 31 * 31 * 31 * h + 31 * 31 * 31 * val[i] + 31 * 31
* val[i + 1] + 31 * val[i + 2] + val[i + 3];
}
for (; i < val.length; i++) {
h = 31 * h + val[i];
}
return h;
}
FYI: http://lemire.me/blog/2015/10/22/faster-hashing-without-effort/
hashCode()
的原因是在不同的JVM间,其结果不能保证一致性(参见Object.hashCode文档)。如果您需要将哈希值存储在某处或期望得到可重复的结果,请使用标准的哈希算法。 - Danny G
MessageDigest
达到每秒100MB以上的速度。即使是随机的MD5哈希碰撞的概率也很小,如果您有“数十亿”份文件,您更可能在读这篇文章的“精确秒数”内被陨石击中。对于唯一标识,MD5已经足够了。只有当它用于安全目的且有人明确试图造成碰撞时,您才需要担心(对于URL而言,这将非常困难)。 - WhiteFang34