任意の長さの文字列を取り、10文字未満のハッシュを生成できる暗号化の方法はありますか?ランダムではなく、メッセージの内容に基づいて、合理的に一意のIDを生成します。
ただし、任意の長さの文字列が不可能な場合は、メッセージを整数値に制限することができます。ただし、その場合、2つの連続した整数でハッシュが類似していてはなりません。
一般的に利用可能なハッシュアルゴリズム(SHA-1など)を使用できます。これにより、必要な結果よりもわずかに長い結果が得られます。結果を必要な長さに切り捨てるだけで十分です。
たとえば、Pythonの場合:
>>> import hashlib
>>> hash = hashlib.sha1("my message".encode("UTF-8")).hexdigest()
>>> hash
'104ab42f1193c336aa2cf08a2c946d5c6fd0fcdb'
>>> hash[:10]
'104ab42f11'
意図的な変更に強いアルゴリズムを必要としない場合、かなり短い(〜8文字)結果を生成する adler32 というアルゴリズムを見つけました。こちらのドロップダウンから選択して試してください:
ダイジェストを作成するには、コンテンツをハッシュする必要があります。多くのハッシュを使用できますが、結果セットの10文字はかなり小さいです。昔、人々はCRC-32を使用しました。これは33ビットのハッシュ(基本的に4文字と1ビット)を生成します。 65ビットハッシュを生成するCRC-64もあります。 128ビットハッシュ(16バイト/文字)を生成するMD5は、同じハッシュを持つ2つのメッセージを見つけることができるため、暗号化の目的で壊れていると見なされます。言うまでもなく、任意の長さのメッセージから16バイトのダイジェストを作成すると、重複することになります。ダイジェストが短いほど、衝突のリスクが大きくなります。
ただし、ハッシュが2つの連続したメッセージ(整数かどうか)で類似していないという懸念は、すべてのハッシュで当てはまります。元のメッセージの1ビットの変更でさえ、非常に異なる結果のダイジェストを生成するはずです。
したがって、CRC-64(および結果のbase-64 ')のようなものを使用すると、探している近所に行くことができます。
MD5(128ビット)やSHA1(160)などの短いものを生成する既存のハッシュアルゴリズムを使用できます。次に、ダイジェストのセクションを他のセクションとXORすることにより、さらに短縮できます。これにより衝突の可能性が高まりますが、ダイジェストを切り捨てるほど悪くはありません。
また、元のデータの長さを結果の一部として含めて、より一意にすることもできます。たとえば、MD5ダイジェストの前半と後半をXORすると、64ビットになります。データの長さに32ビットを追加します(長さが常により少ないビットに収まることがわかっている場合はそれより小さくします)。その結果、96ビット(12バイト)の結果が得られ、24文字の16進文字列に変換できます。または、Base 64エンコードを使用してさらに短くすることもできます。
私に役立つ回答を要約するだけです(base-64エンコーディングの使用に関する@erasmospunkのコメントに注意してください)。私の目標は、ほとんどユニークな短い文字列を持つことでした...
私は専門家ではないので、明白なエラーがある場合はこれを修正してください(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
は、16進文字(hash.hexdigest()
を使用した場合に得られるもの)以上を使用しているため、衝突する可能性は低くなります(つまり、切り捨てるよりも安全である必要があります) 16進ダイジェスト)。
注:UUID4(ランダム)を使用します。他のタイプについては http://en.wikipedia.org/wiki/Universally_unique_identifier をご覧ください。
必要な場合"sub-10-character hash"
8文字のハッシュ(32ビット)を生成するFletcher-32アルゴリズム、CRC-32またはAdler-32。
CRC-32は、Adler32よりも20〜100%遅いです。
Fletcher-32はAdler-32よりもわずかに信頼性があります。 Adlerチェックサムよりも計算コストが低くなります: Fletcher対Adler比較 。
いくつかのフレッチャー実装を備えたサンプルプログラムを以下に示します。
#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ビットのカバレッジが低いためです。これをチェックして:
PHP、Javascript、Pythonなどの実装を持つhashidsライブラリを使用できます。詳細については、 このリンク を確認してください。
最近、単純な文字列削減関数のラインに沿って何かが必要でした。基本的に、コードは次のようになりました(先の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を別の素数に変更します)。このスニペットの興味深い特徴の1つは、SrcがDestより短い場合、Destは入力文字列をそのまま(0 * 37 + value = value)で終わることです。プロセスの最後に「読み取り可能」であるNormalizeは、衝突の増加を犠牲にして、変換されたバイトを調整します。
ソース:
https://github.com/cubiclesoft/cross-platform-cpp/blob/master/sync/sync_util.cpp
これをターミナルで実行するだけです(MacOSまたはLinux):
crc32 <(echo "some string")
8文字の長さ。