36

我想知道 Java 哈希算法的最佳和最快实现,尤其是 MD5 和 SHA-2 512 (SHA512) 或 256。我想要一个函数来获取字符串作为参数并返回哈希作为结果。谢谢你。

编辑:这是为了将每个 URL 映射到唯一的哈希值。由于 MD5 在这方面并不可靠,我更感兴趣的是找到 SHA-2 算法的最佳和最快的实现。请注意,我知道即使是 SHA-2 也可能会为某些 URL 生成相同的哈希值,但我可以接受。

4

6 回答 6

53

第一件事:速度被高估了。您应该在声明给定算法“太慢”之前采取措施。大多数时候,哈希函数的速度无论如何都没有明显的差异。如果你对安全性有疑虑,那么首先选择一个足够安全的哈希函数,然后只担心性能。

此外,您想要散列“字符串”。Java在内部是代表 Unicode 代码点(实际上是使用 UTF-16 对代码点进行编码的 Unicode 16 位代码单元)String的值数组中的一个块。char散列函数将位或字节序列作为输入。所以你必须做一个转换步骤,例如str.getBytes("UTF-8"),将你的字符串作为一串字节获取。与散列本身相比,转换步骤可能会产生不可忽略的成本。

注意:当心 URL 编码!在 URL 中,一些字节可以替换为以 ' %' 符号开头的序列;这是为了支持不可打印的字符,但它也可以用于“标准”字符(例如,将' a'替换为' %61')。这意味着两个不同的字符串(在String.equals()某种意义上)实际上可能表示相同的 URL(就 URL 处理而言)。根据您的情况,这可能是也可能不是问题。

您应该首先尝试将 Java 的MessageDigestAPI 与标准(已安装的)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 缓存的影响会对性能产生巨大影响,并且无法通过获取函数代码并单独运行它来准确预测。

于 2011-03-28T12:33:40.660 回答
21

编辑:我最初将这个问题读作什么是“最快的哈希算法”,并且已被澄清为“每种算法的最快实现”。这是一个有效的问题,其他人指出了更快的实现。但是,除非您在短时间内散列大量数据,否则它根本不会很重要。我怀疑使用标准 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();
于 2011-03-26T23:28:47.090 回答
2

看看这些:很多 SHA / MD5 示例

另外: 来自同一线程:Fast MD5

字符串哈希 = MD5.asHex(MD5.getHash(new File(filename)));

于 2011-03-26T23:24:45.403 回答
2

要考虑的另一件事是使用 MD4。它不如 MD5 安全,但计算速度更快。一直到 XP 的 Windows 都使用 MD4 存储和交换密码,所以我们使用这个哈希,因为它仍然允许我们向这个平台提供身份验证服务。

于 2011-03-27T00:43:59.000 回答
2

考虑 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://blake2.net/

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 更快。

于 2018-05-30T18:41:17.010 回答
0

对于字符串,只需调用 ,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/

于 2016-04-24T01:41:50.920 回答