产生短哈希的哈希函数?

81 encryption uniqueidentifier

是否有单向加密,可以采用任意长度的字符串并产生一个10字符以下的哈希?我想生成合理的唯一ID,但是基于消息内容,而不是随机的.

但是,如果不能使用任意长度的字符串,我可以将消息约束为整数值.但是,在这种情况下,对于两个连续的整数,散列必须不相似.

Gre*_*ill 67

您可以使用任何常用的哈希算法(例如SHA-1),这会给您一个比您需要的结果略长的结果.只需将结果截断为所需的长度,这可能足够好.

例如,在Python中:

>>> import hashlib
>>> hash = hashlib.sha1("my message".encode("UTF-8")).hexdigest()
>>> hash
'104ab42f1193c336aa2cf08a2c946d5c6fd0fcdb'
>>> hash[:10]
'104ab42f11'
Run Code Online (Sandbox Code Playgroud)

  • @erasgreenunk:使用base64进行编码对碰撞阻力没有任何作用,因为如果`hash(a)`与`hash(b)`碰撞,那么`base64(hash(a))`也会碰撞`base64(hash(b))` . (137认同)
  • 这不会在很大程度上增加碰撞的风险吗? (73认同)
  • @GregHewgill你是对的,但我们并没有谈到原始的哈希算法碰撞(是的,`sha1`碰撞,但这是另一个故事).如果你有一个10个字符的哈希,如果它用`base64` vs`base16`(或hex)编码,你得到更高的熵.多高?使用`base16`,每个字符可以得到4位信息,`base64`这个数字是6bits/char.Totaly 10个字符"十六进制"哈希将具有40位熵,而base64 60位.因此,如果我不是非常清楚的话,它会更加耐用,对不起. (53认同)
  • @erasmospunk:哦,我明白了你的意思,是的,如果你的结果有一个有限的固定大小,那么你可以用base64编码和十六进制编码打包更多的重要位. (18认同)
  • 任何合理的哈希函数都可以被截断. (2认同)
  • 或者,如果您担心空间,则根本无法编码为 ASCII 并使用原始字节。 (2认同)

B T*_*B T 39

如果你不需要一个强烈反对故意修改的算法,我发现了一个名为adler32的算法,它可以产生相当短的(~8个字符)结果.从下拉列表中选择它以试用它:

http://www.sha1-online.com/

  • @Mascarpone"减少弱点" - 再次,*什么*弱点?为什么你认为这个算法不是100%完美的OP用法? (6认同)
  • @Mascarpone“不是很可靠”-来源?它有局限性,如果您了解它们,那么它的年龄就无关紧要。 (5认同)
  • 为什么这个解决方案被低估?对我来说,它看起来是一个非常有效的答案. (4认同)
  • 它很旧,不是很可靠。 (3认同)
  • @Mascarpone OP并没有说他们想要一个加密级哈希.OTOH,Adler32是校验和,而不是散列,所以它可能不合适,这取决于OP实际上在做什么. (3认同)
  • Adler32有一个警告,引用[Wikipedia](https://zh.wikipedia.org/wiki/Adler-32):* Adler-32对于具有几百个字节的短消息有一个弱点,因为这些消息的校验和对32个可用位的覆盖率很低。* (2认同)

fer*_*ran 13

您可以使用Python的hashlib库。的shake_128shake_256算法提供可变长度的散列。这是一些工作代码(Python3):

import hashlib
>>> my_string = 'hello shake'
>>> hashlib.shake_256(my_string.encode()).hexdigest(5)
'34177f6a0a'
Run Code Online (Sandbox Code Playgroud)

请注意,使用长度参数x(示例中为 5),该函数返回长度为2x的哈希值。


sg7*_*sg7 12

如果您需要,"sub-10-character hash" 您可以使用Fletcher-32算法,该算法产生 8 个字符哈希(32 位)、 CRC-32Adler-32

CRC-32 比 Adler32 慢 20% - 100%。

Fletcher-32 比 Adler-32 稍微可靠一些。它的计算成本低于 Adler 校验和:Fletcher vs Adler 比较

下面给出了一个带有一些 Fletcher 实现的示例程序:

    #include <stdio.h>
    #include <string.h>
    #include <stdint.h> // for uint32_t

    uint32_t fletcher32_1(const uint16_t *data, size_t len)
    {
            uint32_t c0, c1;
            unsigned int i;

            for (c0 = c1 = 0; len >= 360; len -= 360) {
                    for (i = 0; i < 360; ++i) {
                            c0 = c0 + *data++;
                            c1 = c1 + c0;
                    }
                    c0 = c0 % 65535;
                    c1 = c1 % 65535;
            }
            for (i = 0; i < len; ++i) {
                    c0 = c0 + *data++;
                    c1 = c1 + c0;
            }
            c0 = c0 % 65535;
            c1 = c1 % 65535;
            return (c1 << 16 | c0);
    }

    uint32_t fletcher32_2(const uint16_t *data, size_t l)
    {
        uint32_t sum1 = 0xffff, sum2 = 0xffff;

        while (l) {
            unsigned tlen = l > 359 ? 359 : l;
            l -= tlen;
            do {
                sum2 += sum1 += *data++;
            } while (--tlen);
            sum1 = (sum1 & 0xffff) + (sum1 >> 16);
            sum2 = (sum2 & 0xffff) + (sum2 >> 16);
        }
        /* Second reduction step to reduce sums to 16 bits */
        sum1 = (sum1 & 0xffff) + (sum1 >> 16);
        sum2 = (sum2 & 0xffff) + (sum2 >> 16);
        return (sum2 << 16) | sum1;
    }

    int main()
    {
        char *str1 = "abcde";  
        char *str2 = "abcdef";

        size_t len1 = (strlen(str1)+1) / 2; //  '\0' will be used for padding 
        size_t len2 = (strlen(str2)+1) / 2; // 

        uint32_t f1 = fletcher32_1(str1,  len1);
        uint32_t f2 = fletcher32_2(str1,  len1);

        printf("%u %X \n",    f1,f1);
        printf("%u %X \n\n",  f2,f2);

        f1 = fletcher32_1(str2,  len2);
        f2 = fletcher32_2(str2,  len2);

        printf("%u %X \n",f1,f1);
        printf("%u %X \n",f2,f2);

        return 0;
    }
Run Code Online (Sandbox Code Playgroud)

输出:

4031760169 F04FC729                                                                                                                                                                                                                              
4031760169 F04FC729                                                                                                                                                                                                                              

1448095018 56502D2A                                                                                                                                                                                                                              
1448095018 56502D2A                                                                                                                                                                                                                              
Run Code Online (Sandbox Code Playgroud)

同意测试向量

"abcde"  -> 4031760169 (0xF04FC729)
"abcdef" -> 1448095018 (0x56502D2A)
Run Code Online (Sandbox Code Playgroud)

Adler-32 对几百字节的短消息有一个弱点,因为这些消息的校验和对 32 个可用位的覆盖很差。检查这个:

Adler32 算法不够复杂,无法与可比较的校验和竞争


Joh*_*ohn 11

您需要散列内容以提供摘要.有许多哈希可用,但结果集中10个字符非常小.回过头来,人们使用CRC-32,它产生33位散列(基本上是4个字符加1位).还有CRC-64产生65位散列.产生128位散列(16字节/字符)的MD5因加密目的而被视为已损坏,因为可以找到具有相同散列的两条消息.不用说,任何时候你从任意长度的消息中创建一个16字节的摘要,你最终会得到重复.摘要越短,碰撞的风险就越大.

但是,对于两个连续消息(无论是否为整数),散列不相似的问题应该适用于所有散列.即使原始消息中的单个位改变也应该产生截然不同的结果摘要.

因此,使用像CRC-64(以及结果的基础64)这样的东西可以让你进入你正在寻找的邻居.

  • 没有丝毫. (5认同)
  • "但是,你担心两个连续消息的散列不相似[...]应该适用于所有散列." - 这不一定是真的.例如,对于用于聚类或克隆检测的哈希函数,实际上恰恰相反:您*希望*类似的文档产生类似(或甚至相同)的哈希值.一个众所周知的散列算法示例是Soundex,它被*专门设计为产生相似输入的相同值. (5认同)

JJ *_*wax 8

只是总结一个对我有用的答案(注意@erasmospunk有关使用base-64编码的评论)。我的目标是要有一个短串,这串串大都是独特的...

我不是专家,因此,如果有任何明显的错误,请更正此错误(在Python中再次像接受的答案一样):

import base64
import hashlib
import uuid

unique_id = uuid.uuid4()
# unique_id = UUID('8da617a7-0bd6-4cce-ae49-5d31f2a5a35f')

hash = hashlib.sha1(str(unique_id).encode("UTF-8"))
# hash.hexdigest() = '882efb0f24a03938e5898aa6b69df2038a2c3f0e'

result = base64.b64encode(hash.digest())
# result = b'iC77DySgOTjliYqmtp3yA4osPw4='
Run Code Online (Sandbox Code Playgroud)

result这里使用不止十六进制字符(如果你使用你会得到什么hash.hexdigest()),所以它不太可能有冲突(即,应该是较安全十六进制消化截断)。

注意:使用UUID4(随机)。有关其他类型,请参见http://en.wikipedia.org/wiki/Universally_unique_identifier


sgo*_*n00 7

只需在终端(在 MacOS 或 Linux 上)运行它:

crc32 <(echo "some string")
Run Code Online (Sandbox Code Playgroud)

8 个字符长。


dyn*_*ael 6

您可以使用现有的哈希算法来生成短的内容,例如MD5(128位)或SHA1(160).然后,您可以通过将摘要的部分与其他部分进行异或来进一步缩短.这将增加碰撞的机会,但不会像简单地截断摘要那样糟糕.

此外,您可以将原始数据的长度作为结果的一部分,以使其更加独特.例如,使用后半部分对MD5摘要的前半部分进行异或将导致64位.为数据长度添加32位(如果知道长度总是适合更少的位,则更低).这将导致96位(12字节)结果,然后您可以将其转换为24个字符的十六进制字符串.或者,您可以使用base 64编码使其更短.

  • FWIW,这被称为异或折叠。 (3认同)

sor*_*bet 5

现在是 2019 年,有更好的选择。即,xxhash

~ echo test | xxhsum                                                           
2d7f1808da1fa63c  stdin
Run Code Online (Sandbox Code Playgroud)

  • 为什么更好? (4认同)
  • 此链接已损坏。最好提供更完整的答案。 (3认同)
  • 链接现在有效。 (2认同)