有没有一种加密方法可以将任意长度的字符串转换成小于10个字符的哈希值?我想基于消息内容生成相对独特的ID,而不是随机生成。
如果不可能处理任意长度的字符串,我可以接受将消息限制为整数值。但在这种情况下,哈希值不能对两个连续的整数太相似。
有没有一种加密方法可以将任意长度的字符串转换成小于10个字符的哈希值?我想基于消息内容生成相对独特的ID,而不是随机生成。
如果不可能处理任意长度的字符串,我可以接受将消息限制为整数值。但在这种情况下,哈希值不能对两个连续的整数太相似。
>>> 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))
发生碰撞。 - Greg Hewgillsha1
碰撞了,但这是另一回事)。
如果您有一个由base64
编码的10个字符的哈希值,则其熵会更高,而不是使用base16
(或十六进制)进行编码。
更高多少? 使用 base16
您每个字符获得4位信息, 而使用 base64
则为每个字符6位。因此,一个由10个字符组成的“hex”哈希将具有40位熵,而base64则为60位。因此,它略微更加抗性强,如果我没有表达清楚,请见谅。 - John L. Jegutanis如果您不需要一个强防意外修改的算法,我找到了一个名为adler32的算法,它可以产生相当短(~8个字符)的结果。请从这里的下拉菜单中选择该算法以尝试:
你可以使用Python的hashlib库。其中,shake_128和shake_256算法提供了可变长度的哈希值。以下是一些可以运行的代码(Python3):
>>> import hashlib
>>> my_string = 'hello shake'
>>> hashlib.shake_256(my_string.encode()).hexdigest(5)
'34177f6a0a'
请注意,使用长度参数x(例如5),该函数返回长度为2x的哈希值。
总结一下对我有帮助的回答(注意@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了解其他类型。
如果您需要“子10字符哈希”,可以使用Fletcher-32算法,该算法生成8个字符的哈希值(32位),还可以使用CRC-32或Adler-32。
CRC-32比Adler32慢20%至100%。
Fletcher-32比Adler-32稍微更可靠。它的计算成本低于Adler校验和:Fletcher vs Adler comparison。
下面是一个具有几个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位中可用位数较少。请参阅:
现在已经是2019年,有更好的选择。即xxhash。
~ echo test | xxhsum
2d7f1808da1fa63c stdin
在 MacOS 或 Linux 终端中运行以下命令:
crc32 <(echo "some string")
长度为8个字符。
最近我需要一个简单的字符串缩减函数。基本上,代码看起来像这样(以下是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
?如果你想要比int输出更大的碰撞抵抗力,你应该使用SHA。 - Navin