有没有一种加密方法可以采用任意长度的字符串并产生一个小于10个字符的哈希值?我想生成合理唯一的ID,但要基于消息内容,而不是随机生成。
但是,如果不可能使用任意长度的字符串,我可以将消息限制为整数值。但是,在这种情况下,对于两个连续的整数,散列一定不能相似。
有没有一种加密方法可以采用任意长度的字符串并产生一个小于10个字符的哈希值?我想生成合理唯一的ID,但要基于消息内容,而不是随机生成。
但是,如果不可能使用任意长度的字符串,我可以将消息限制为整数值。但是,在这种情况下,对于两个连续的整数,散列一定不能相似。
Answers:
您可以使用任何常用的哈希算法(例如SHA-1),这将使您的结果比所需的结果稍长。只需将结果截断为所需的长度,这可能就足够了。
例如,在Python中:
>>> import hashlib
>>> hash = hashlib.sha1("my message".encode("UTF-8")).hexdigest()
>>> hash
'104ab42f1193c336aa2cf08a2c946d5c6fd0fcdb'
>>> hash[:10]
'104ab42f11'
hash(a)
与发生碰撞,hash(b)
那么base64(hash(a))
也会与发生碰撞base64(hash(b))
。
sha1
冲突,但这是另一个故事)。如果您使用10个字符的哈希值,则使用base64
vs base16
(或十六进制)编码时,熵值会更高。多高?随着base16
你的每个字符的信息4位,base64
这个数字是6比特/字符。总共10个字符的“十六进制”哈希将具有40位的熵,而base64为60位。因此,它的抵抗力稍强,如果我不太清楚的话,抱歉。
如果您不需要强大的针对故意修改的算法,那么我发现一种叫做adler32的算法可以产生较短的(约8个字符)结果。从这里的下拉列表中选择它来尝试:
您需要对内容进行哈希处理以得出摘要。可用的哈希很多,但结果集的10个字符很小。追溯到过去,人们使用了CRC-32,它会产生33位的哈希值(基本上是4个字符加一位)。还有CRC-64产生65位哈希值。出于加密目的,产生128位哈希(16个字节/字符)的MD5被认为是损坏的,因为可以找到两个具有相同哈希的消息。不用说,任何时候从任意长度的消息中创建一个16字节的摘要时,都会导致重复。摘要越短,发生碰撞的风险就越大。
但是,您担心两个连续消息(无论是否为整数)的哈希值都不相似,所有散列都应为真。即使原始消息中的一点点更改也将产生截然不同的结果摘要。
因此,使用CRC-64之类的方法(对结果进行base-64处理)应该可以使您进入所需的社区。
只是总结一个对我有用的答案(注意@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='
在result
这里使用不止十六进制字符(如果你使用你会得到什么hash.hexdigest()
),所以它不太可能有冲突(即,应该是较安全十六进制消化截断)。
注意:使用UUID4(随机)。有关其他类型,请参见http://en.wikipedia.org/wiki/Universally_unique_identifier。
您可以使用现有的散列算法来生成较短的内容,例如MD5(128位)或SHA1(160)。然后,您可以通过将摘要的各个部分与其他部分进行异或来进一步缩短该时间。这将增加碰撞的机会,但不像简单地截断摘要那样糟糕。
另外,您可以将原始数据的长度作为结果的一部分,以使其更具唯一性。例如,将MD5摘要的前半部分与后半部分异或将产生64位。添加32位作为数据的长度(如果您知道该长度总是适合较少的位,则增加32位)。这将导致96位(12字节)的结果,然后您可以将其转换为24个字符的十六进制字符串。或者,您可以使用base 64编码使其更短。
如果需要"sub-10-character hash"
,可以使用Fletcher-32算法,该算法产生8个字符的哈希(32位), CRC-32或Adler-32。
CRC-32比Adler32慢20%-100%。
Fletcher-32比Adler-32更加可靠。它的计算成本比Adler校验和低:Fletcher与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;
}
输出:
4031760169 F04FC729
4031760169 F04FC729
1448095018 56502D2A
1448095018 56502D2A
同意测试向量:
"abcde" -> 4031760169 (0xF04FC729)
"abcdef" -> 1448095018 (0x56502D2A)
Adler-32对于具有几百个字节的短消息有一个弱点,因为这些消息的校验和对32个可用位的覆盖率很差。检查一下:
最近,我需要一些简单的字符串缩减功能。基本上,代码看起来像这样(前面的C / C ++代码):
size_t ReduceString(char *Dest, size_t DestSize, const char *Src, size_t SrcSize, bool Normalize)
{
size_t x, x2 = 0, z = 0;
memset(Dest, 0, DestSize);
for (x = 0; x < SrcSize; x++)
{
Dest[x2] = (char)(((unsigned int)(unsigned char)Dest[x2]) * 37 + ((unsigned int)(unsigned char)Src[x]));
x2++;
if (x2 == DestSize - 1)
{
x2 = 0;
z++;
}
}
// Normalize the alphabet if it looped.
if (z && Normalize)
{
unsigned char TempChr;
y = (z > 1 ? DestSize - 1 : x2);
for (x = 1; x < y; x++)
{
TempChr = ((unsigned char)Dest[x]) & 0x3F;
if (TempChr < 10) TempChr += '0';
else if (TempChr < 36) TempChr = TempChr - 10 + 'A';
else if (TempChr < 62) TempChr = TempChr - 36 + 'a';
else if (TempChr == 62) TempChr = '_';
else TempChr = '-';
Dest[x] = (char)TempChr;
}
}
return (SrcSize < DestSize ? SrcSize : DestSize);
}
它可能有比预期更多的冲突,但它不打算用作加密哈希函数。如果冲突太多,您可以尝试各种乘数(即将37更改为另一个质数)。此代码段有趣的功能之一是,当Src短于Dest时,Dest最终以原样输入字符串(0 * 37 + value = value)。如果您希望在过程结束时“读取”某些内容,Normalize将以增加冲突为代价来调整转换后的字节。
资源:
https://github.com/cubiclesoft/cross-platform-cpp/blob/master/sync/sync_util.cpp
DestSize
大于4(32位)时,哈希本身是如此糟糕?如果希望输出大于int的输出提供的耐碰撞性,则可以使用SHA。