【问题标题】:Is it possible to limit the hashcode into specific number of characters in Java是否可以将哈希码限制为 Java 中特定数量的字符
【发布时间】:2013-11-11 11:55:23
【问题描述】:

我已经编写了一个使用 MD5 算法将纯文本转换为其哈希码的方法。请在下面找到我使用的代码。

public static String convertToMD5Hash(final String plainText){
            MessageDigest messageDigest = null;

            try {
                messageDigest = MessageDigest.getInstance("MD5");
            } catch (NoSuchAlgorithmException e) {
                LOGGER.warn("For some wierd reason the MD5 algorithm was not found.", e);
            }

            messageDigest.reset();
            messageDigest.update(plainText.getBytes());
            final byte[] digest = messageDigest.digest();
            final BigInteger bigInt = new BigInteger(1, digest);
            String hashtext = bigInt.toString(8);

            return hashtext;
}

此方法运行良好,但它返回一个冗长的散列。我需要将此哈希文本限制为 8 个字符。有没有办法在 Java 中设置哈希码的长度?

【问题讨论】:

  • 它是一个散列,只需使用生成的字符串的 8 个字符(任何你想要的。Avery 选择应该和其他任何选择一样好)
  • 我需要像缩小/压缩哈希码这样的东西,而不是参与其中。 :)
  • @zulox,MD5 是一个加密哈希,没有必要专门缩小或压缩它,您可以只取前 8 个十六进制半字节或以其他方式取前缀。你不会赢得任何东西,例如通过对所有 8 个半字节块进行异或运算,不会增加安全性或随机性。
  • messageDigest.update(plainText.getBytes()); 如果您的平台的默认编码更改,这将不起作用。使用getBytes(StandardCharsets.UTF_8)
  • 8 个十六进制编码字符?

标签: java algorithm security md5 message-digest


【解决方案1】:

是和否。如果您总是削减原​​始哈希字符串的相似性(即 8 个最后/第一个字符),则可以使用原始哈希的子字符串。你打算用那个“半散列”做什么是另一回事。

无论您要做什么,请确保它与安全无关。

原因如下:MD5 是 128 位哈希,因此有 2^128 = ~340,000,000,000,000,000,000,000,000,000,000,000,000 种可能的排列。相当数量的排列使得暴力破解这种字符串几乎是不可能的。通过减少到 8 个字符,您将得到 32 位散列。这是因为单个十六进制值需要 4 位来表示(因此,128 位 / 4 位 = 32 个十六进制值)。使用 32 位哈希,只有 2^32 = 4,294,967,296 种组合。这大约比原始 128 位哈希安全性低 79,228,162,514,264,337,593,543,950,336 倍,并且可以在几秒钟内被任何具有 80 年代计算器处理能力的旧计算机破解。

【讨论】:

  • 为了澄清,这里的“破碎”意味着发现碰撞。
  • 你的 -1 来自哪里?
  • @TomHawtin-tackline 好点。我想我把它和整数的最小值/最大值计算混合在一起了。
  • 假设如果我从哈希码中获得前 8 个字符,我能保证我不会为不同的纯文本获得相同的前 8 个字符值吗?我的意思是,前 8 个字符会的哈希码是唯一的吗?
  • @zulox No. 哈希可能有冲突,即。不同的散列字符串可能具有相同的散列。但是,如果使用 128 位散列,则冲突的可能性会大大降低(“天文数字最小的可能性”)。在您的 8-character = 32-bit 情况下,冲突可能很容易成为可能的事件(当然取决于散列的数量)
【解决方案2】:

没有。 MD5 被定义为返回 128 位值。您可以使用 Base64 将它们编码为 ASCII 并使用 String#substring(0, 8) 截断它。

在 Java 8(尚未正式发布)中,您可以将 byte[] 编码为 Base64,如下所示:

String base64 = Base64.getEncoder().encodeToString(digest);

有关早期 Java 版本,请参阅 Decode Base64 data in Java

【讨论】:

    【解决方案3】:

    只要数据的任何部分发生变化,所有散列算法都应该随机更改整个散列中的位。所以你可以从你的哈希中选择 8 个字符。只是不要随意挑选它们 - 它必须是可重现的

    【讨论】:

      【解决方案4】:

      首先正如大家所提到的,64 位哈希不够安全。最终,这取决于您究竟打算如何处理哈希。

      如果您仍需要将其转换为 8 个字符,我建议使用 BigIteger.longValue() 将 BigInteger 向下转换为 Long 值

      它将确保它产生的长值与产生的散列一致。

      我不确定从 128 位哈希中提取最高有效的 64 位是否是个好主意。我宁愿采用最低有效的 64 位。这确保了

      hash(128, a) = hash(128, b)hash(64, a) = hash(64, b) 将永远为真。

      但我们必须忍受 64 位的冲突,即当hash(64, a) = hash(64, b) 然后hash(128, a) = hash(128, b) 并不总是正确的。

      简而言之,我们确保不会出现 2 个文本的 128 位哈希值不同但它们的 64 位哈希值相同的情况。这取决于您真正使用哈希的目的,但我个人认为这种方法更正确。

      【讨论】:

        猜你喜欢
        • 2011-01-28
        • 2021-11-23
        • 2020-12-19
        • 1970-01-01
        • 1970-01-01
        • 2018-07-31
        • 2019-08-02
        • 2013-09-15
        • 1970-01-01
        相关资源
        最近更新 更多