![](https://habrastorage.org/storage2/112/dc2/20c/112dc220cc2982a5a09fe4e3bfbcf4e4.jpg)
このメソッドの中核は、 ハッシュ関数と呼ばれる数学的オブジェクトです。 小さな教育プログラムとして 、 以前の記事も学習することをお勧めします 。
ハッシュ関数については、かなり興味深い誤解がいくつかあります。
- ハッシュ関数は簡単に元に戻すことができます。 伝えられるところでは、md5cryptの結果を復元する特定のプログラムmd5decryptがあります。
- MD5のようなハッシュ関数は長い間壊れていました。 与えられた線で衝突を起こす-些細な問題;
- ハッシュ関数は暗号化アルゴリズムです。
これは明らかにナンセンスです。 暗号化ハッシュ関数は、定義上、扱いが難しく、現在使用されているメソッド(MD5、SHA)については、他に誰も証明していません。 最も一般的な形式でのみ衝突を構築し、f(s1)= f(s2)のような異なるラインs1、s2を生成することを学びました。 暗号化キーの概念がないという理由だけで、ハッシュは暗号化ではありません。
しかし、時には「ナンセンス」が興味深い解決策を構築するのに役立つ場合があります。上記の論文が真実であることを想像してみましょう。 そうでなくても、それらが実行されるオブジェクトを構築しましょう。
R(キー、メッセージ)= md5の最初のNビット(キー+メッセージ)とします。ここで、Nはそれほど大きくありません。40に等しいとしましょう。そして、ここからが楽しみです!
まず、特定のキーキーに対して、関数の目的の値を提供する多数の異なるメッセージを簡単に作成できます。 約2 ^ 40の異なる行をソートすることは、通常の「ホーム」コンピューターにとって実行可能なタスクです。 関数Rを適用した結果であるXの値がある場合、逆像を簡単に見つけることができます。
つまり、X = R( 'test_key'、 'msg')= 5B7AF38712とする
メッセージ 'msg'を忘れ、ハッシュ値5B7AF38712とソースキー 'test_key'のみがあり、検索を開始し、しばらくすると、文字列 'msg'をプレイメージとして取得します!
しかし、キー( 'test_key')がわからず、ハッシュ値しかわからない場合はどうでしょうか? 次に、X値を提供するキーとメッセージのペアを無限に構築できます。
これらのペアのいくつかは、それが何を意味するにせよ、意味のあるものにさえ見えます。 さらに、そのようなペアの1つは、宇宙の生命とそのすべて(いくつかの言い回しで)とその答えの主な質問になります。
なぜそう 入力値(キーとメッセージ)のセットが無限であり、関数Rが可能な値の数が有限であり、それぞれの(非常に大雑把に言えば)発生する可能性が等しいからです。
今、私はプロセスの考えが理解できると思います-キー( 'test_key')を知っている人 '5B7AF38712'を送信すると、メッセージ( 'msg')を受信する可能性が高いです(ただし、これに関する保証はありません、注意してください)。 そして、知らない-ゴミ。
魔法
次に、受信者が特定の保証でメッセージを正しく復号化できるように、メッセージをエンコードする方法を学習します。 それでも、私たちは非常にトリッキーであり、同じパッケージを別のメッセージとして別のキーで復号化することを望んでいます。
つまり、入力で:key1、message1およびkey2、message2。
さらに、crypt(key1、message1、key2、message2)= X、
復号化(X、key1)= message1、復号化(X、key2)= message2
ここで、関数Rを「近代化」する必要があります。関数のセット全体を構築します。
R m (キー、msg)= md5の最初のNビット(キー+行(m)+ msg)、
ここで、文字列(m)は文字列としてのmの一意の表現です(たとえば、10進表記)。
インデックスmは、メッセージを暗号化するときに変化する特別な「制御値」です(つまり、アルゴリズムによって選択されます)。
それがどのように選択されるかを伝える前に、非常に簡単なので、復号化手順を提供します:
復号化(X、キー)= msgの値、R(キー、msg)== X、最小m
反復形式では、次のように記述できます(擬似コード):
string decrypt(X, key) { for (int m = 0; m < MAX_INT; m++) for (string msg in generate_all_strings(MAX_STRING_LENGTH) ) if (first_N_bytes( md5( key + m.ToString() + msg) ) == X) // R() return msg; }
ここに、新しいパラメータMAX_STRING_LENGTHが表示されます 。これは、ブルートフォースによって取得できる文字列の最大長を担当します。 たとえば、元のメッセージが「hello、Habrahabr」の場合、適切な列挙パフォーマンスに制限することは理にかなっています。暗号化文字列自体は、「hell」、「o、H」、「abra」、「habr」などの短いシーケンスに分解する必要がありますキーの交替を忘れずに、それらのそれぞれで暗号化機能を試してください。
最初の40ビットの衝突を取得することは難しくなく、この関数は値を返すことが保証されています。 「暗号化」機能のタスクは、目的の値が確実に返されるようにすることです。
しかし、これはブルートフォースによっても実行できます。
string encrypt(key, msg) { for (int m = 0; m < MAX_INT; m++) { string X = first_N_bytes( md5( key + m.ToString() + msg) ); // R() if (decrypt(X, key) == msg) return X; } }
したがって、さまざまなパラメーターを使用してモデル化することにより、正しいデコードの信頼性を提供します。 復号化アルゴリズムが変わらない場合、これは正確性の保証であり、チェックサム、バリデーター、正しい復号化の事実を「与える」ことを可能にする他のことはありません!
最初に、一度に2つのメッセージを暗号化する可能性について話しましたが、実際には今ではそれが自明の事実になりつつあります。
string encrypt(key, msg, key2, msg2) { for (int m = 0; m < MAX_INT; m++) { string X = first_N_bytes( md5( key + m.ToString() + msg) ); // R() if (decrypt(X, key) == msg && decrypt(X, key2) == msg2) return X; } }
ただし、ここにはいくつかの落とし穴があります-「制御値」(m)の固定範囲で-このような関数R mは存在しない可能性があり、範囲を拡張する必要があります。 また、範囲を拡大すると、アルゴリズムの速度が低下します。 一般に、mの値の範囲はハッシュ関数Rの幅よりも大きくする必要があります。そのため、単純にそれを減らすことができます(テストプロトタイプで行いました)。 ただし、出力値の範囲が大幅に縮小されているため、このようなmも存在しない可能性があります。
一般に、プロトタイプを実装するとき、2バイトの切り捨てに決めました。 さらに、パフォーマンスの問題を部分的に解決するために(C#でプロトタイプをすばやくスケッチし、「ローカル」暗号化プロバイダーは非常に遅いです。GPUでの実装よりも約50,000倍遅いので、少し後で実行します)、文字のみを使用するオプションを残しました範囲a〜z。 このような「悲しい」形式では、すでに機能しています。 2つのキーと値のペアを入力し、[Encrypt2]をクリックして、しばらくしてハッシュコードを取得します。 最初のキーを使用して、最初のメッセージにすばやく復号化します。
![](http://habrastorage.org/storage2/d3c/58e/3d8/d3c58e3d8954cf8c35ae3807e89d68a5.png)
2番目の秒では、それぞれ:
![](http://habrastorage.org/storage2/50a/f59/e17/50af59e176b02c41c98d59f9e8c57080.png)
同じ暗号化されたテキストに注意してください。 まあ、間違ったキーで-奇妙なメッセージで(しかし、それが元のものではなかったという保証はどこにありますか?):
![](http://habrastorage.org/storage2/f41/5ca/8ce/f415ca8ced7fd0a53dd0e597622f7c35.png)
こちらからプロトタイプをダウンロードできます: dl.dropbox.com/u/243445/md5h/HashCode.7z
それにはとんでもない制限がたくさんありますが、inしている人にとっては、これは単なるアイデアの例にすぎないことを思い出させてください。 優れたアプリケーションでは、アイデアの関連性に応じて、それについて考えることができます。