任意の長さの文字列を取り、10文字未満のハッシュを生成できる暗号化の方法はありますか?合理的に一意のIDを生成したいが、ランダムではなく、メッセージの内容に基づいています。
メッセージを整数値に制限しても問題ありませんが、任意の長さの文字列は不可能です。ただし、その場合、ハッシュは2つの連続した整数で類似していてはなりません。
任意の長さの文字列を取り、10文字未満のハッシュを生成できる暗号化の方法はありますか?合理的に一意のIDを生成したいが、ランダムではなく、メッセージの内容に基づいています。
メッセージを整数値に制限しても問題ありませんが、任意の長さの文字列は不可能です。ただし、その場合、ハッシュは2つの連続した整数で類似していてはなりません。
回答:
一般的に利用可能なハッシュアルゴリズム(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
(または16進数)でエンコードすると、エントロピーが高くなります。どれくらい高い?ではbase16
であなた、文字あたり4ビットの情報を取得しbase64
、この図は6bits /文字です。合計10文字の「16進数」のハッシュは40ビットのエントロピーを持ち、base64は60ビットです。ですから、少し抵抗力があります、すごくはっきりしていなかったら申し訳ありません。
ダイジェストを作成するには、コンテンツをハッシュする必要があります。利用可能なハッシュは多数ありますが、結果セットの10文字はかなり小さいです。昔、人々は33ビットのハッシュ(基本的に4文字と1ビット)を生成するCRC-32を使用していました。65ビットのハッシュを生成するCRC-64もあります。128ビットハッシュ(16バイト/文字)を生成するMD5は、同じハッシュを持つ2つのメッセージが見つかるため、暗号化の目的で壊れていると見なされます。言うまでもなく、任意の長さのメッセージから16バイトのダイジェストを作成すると、結果として重複が発生します。ダイジェストが短いほど、衝突のリスクが高くなります。
ただし、ハッシュが2つの連続したメッセージ(整数であるかどうかに関係なく)で類似していないという懸念は、すべてのハッシュで当てはまるはずです。元のメッセージが1ビット変更されても、結果として大きく異なるダイジェストが生成されます。
したがって、CRC-64(および結果を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
ここでは、(あなたが使用している場合、あなたが取得したいものをちょうど進文字以上使用しているhash.hexdigest()
ことである(つまり、六角ダイジェストよりも切り捨てることは、より安全でなければなりません)、衝突を持っている可能性が低いですので)。
注:UUID4(ランダム)を使用します。他のタイプについては、http://en.wikipedia.org/wiki/Universally_unique_identifierを参照してください。
MD5(128ビット)やSHA1(160)などの短いものを生成する既存のハッシュアルゴリズムを使用できます。次に、ダイジェストのセクションを他のセクションとXORすることで、さらに短縮できます。これは衝突の可能性を高めますが、単にダイジェストを切り捨てるほど悪くはありません。
また、元のデータの長さを結果の一部として含めて、一意にすることもできます。たとえば、MD5ダイジェストの前半を後半とXORすると64ビットになります。データの長さに32ビットを追加します(または、長さが常により少ないビットに収まることがわかっている場合は、それより低くします)。これにより、96ビット(12バイト)の結果が得られ、24文字の16進文字列に変換できます。または、base 64エンコーディングを使用してさらに短くすることもできます。
必要な"sub-10-character hash"
場合は、8文字のハッシュ(32ビット)を生成するFletcher-32アルゴリズム、 CRC-32またはAdler-32を使用できます。
CRC-32は、Adler32よりも20〜100%低速です。
Fletcher-32は、Adler-32よりもわずかに信頼性が高くなっています。アドラーチェックサムよりも計算コストが低い:フレッチャーとアドラーの比較。
いくつかの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を別の素数に変更します)。このスニペットの興味深い機能の1つは、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を使用します。