我想知道 Java 哈希算法的最佳和最快实现,尤其是 MD5 和 SHA-2 512 (SHA512) 或 256。我想要一个函数来获取字符串作为参数并返回哈希作为结果。谢谢你。
编辑:这是为了将每个 URL 映射到唯一的哈希值。由于 MD5 在这方面并不可靠,我更感兴趣的是找到 SHA-2 算法的最佳和最快的实现。请注意,我知道即使是 SHA-2 也可能会为某些 URL 生成相同的哈希值,但我可以接受。
第一件事:速度被高估了。您应该在声明给定算法“太慢”之前采取措施。大多数时候,哈希函数的速度无论如何都没有明显的差异。如果你对安全性有疑虑,那么首先选择一个足够安全的哈希函数,然后只担心性能。
此外,您想要散列“字符串”。Java在内部是代表 Unicode 代码点(实际上是使用 UTF-16 对代码点进行编码的 Unicode 16 位代码单元)String
的值数组中的一个块。char
散列函数将位或字节序列作为输入。所以你必须做一个转换步骤,例如str.getBytes("UTF-8")
,将你的字符串作为一串字节获取。与散列本身相比,转换步骤可能会产生不可忽略的成本。
注意:当心 URL 编码!在 URL 中,一些字节可以替换为以 ' %
' 符号开头的序列;这是为了支持不可打印的字符,但它也可以用于“标准”字符(例如,将' a
'替换为' %61
')。这意味着两个不同的字符串(在String.equals()
某种意义上)实际上可能表示相同的 URL(就 URL 处理而言)。根据您的情况,这可能是也可能不是问题。
您应该首先尝试将 Java 的MessageDigest
API 与标准(已安装的)JCE 提供程序(即您调用MessageDigest.getInstance("SHA-256")
)一起使用,然后对结果进行基准测试。从理论上讲,JCE 可以将调用映射到使用“本机”代码(用 C 或汇编编写)的实现,这将比使用 Java 更快。
话虽如此...
sphlib是 C 和 Java 中许多加密哈希函数的开源实现。该代码已针对速度进行了优化,实际上,Java 版本比 Sun/Oracle 提供的标准 JRE 更快。如果上一个链接失败,请使用此链接(主主机服务器有时因维护而停机,现在似乎就是这种情况)(警告:10 MB 下载)。该档案还包含一份报告(在 2010 年的第二次 SHA-3 候选人会议上提交),该报告提供了一些在几个平台上测量的性能数据,用于 SHA-2 和即将到来的 SHA-3 的 14 个“第二轮”候选人。
但是您确实应该进行现场基准测试。例如,对 L1 缓存的影响会对性能产生巨大影响,并且无法通过获取函数代码并单独运行它来准确预测。
编辑:我最初将这个问题读作什么是“最快的哈希算法”,并且已被澄清为“每种算法的最快实现”。这是一个有效的问题,其他人指出了更快的实现。但是,除非您在短时间内散列大量数据,否则它根本不会很重要。我怀疑使用标准 JCE 提供的东西以外的东西通常值得花时间和复杂性。
对于 URL 地址,您需要在现代硬件上以每秒一百万以上的 SHA-256 进行散列,以要求更快的速度。我无法想象大多数应用程序每秒需要超过 1000 次(每天超过 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();
要考虑的另一件事是使用 MD4。它不如 MD5 安全,但计算速度更快。一直到 XP 的 Windows 都使用 MD4 存储和交换密码,所以我们使用这个哈希,因为它仍然允许我们向这个平台提供身份验证服务。
考虑 BLAKE2,它比上面提到的散列更快、更安全。
MD5、SHA-1、SHA256 和 SHA-512 容易受到长度扩展的影响。
MD5 和 SHA-1 容易发生冲突。
MD5 容易受到选择前缀冲突的影响。
SHA-3 和 BLAKE2 没有已知的安全问题,可以生成不同长度的摘要。
SHA-3 在硬件中实现时最快;使用软件实现时,BLAKE2 速度最快。
BLAKE2b 针对 64 位平台进行了优化,可生成 1 到 64 字节之间任意大小的摘要。
BLAKE2s 针对 8 到 32 位平台进行了优化,可以生成 1 到 32 字节之间任意大小的摘要。
以下是 AES、MD5、SHA-256 和 BLAKE2b 的基准。
https://www.cryptopp.com/benchmarks.html
在第一个链接中,BLAKE2b (947 Mbits) 比 SHA-256 (413 Mbits) 和 MD5 (632 Mbits) 快得多。
在第二个链接中,AES-256 CBC (805 Mbits) 和 BLAKE2b (776 Mbits) 的速度大致相同,并且比 SHA-256 (275 Mbits) 和 MD5 (602) 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;
}
仅供参考: http: //lemire.me/blog/2015/10/22/faster-hashing-without-effort/