シーザー暗号のような単純なシフト暗号と、より安全な置換暗号を実装しましょう。
「もし彼に秘密にしたいことがあれば、彼はそれを暗号で書きました。つまり、アルファベットの文字の順序を変えることで、一語も読み取れないようにしたのです。もしこれを解読して意味をつかみたいと望むなら、アルファベットの4番目の文字、すなわちAの代わりにDを当てはめ、他の文字についても同じようにしなければなりません。」 (スエトニウス『ユリウス・カエサル伝』)
暗号はとても単純なアルゴリズムで、テキストを読みにくくしながらも、簡単に解読できるようにするものです。さまざまな形の暗号解読に対して弱いのですが、カエサルは幸運でした。彼の敵が暗号解読者ではなかったからです。
シーザー暗号は、ユリウス・カエサルが戦地へ送ったいくつかのメッセージに使われました。カエサルは、この暗号があまり優れていないことを知っていましたが、その点で味方が1人いました。ほとんど誰も文字をうまく読めなかったのです。そのため、数文字ずれていても、知っているわずかな単語を読み取れない程度には十分でした。
課題は、シーザー暗号のような単純なシフト暗号を作ることです。次の画像は、シーザー暗号のよい例です。
![]()
たとえば、次のとおりです。
"iamapandabear"をencode関数への入力として渡すと、暗号"ldpdsdqgdehdu"が返ります。通信中にメッセージを秘密に保つのに十分なほど、不明瞭です。
"ldpdsdqgdehdu"をdecode関数に入れると、元の"iamapandabear"が返り、友人が元のメッセージを読めるようになります。
シフト暗号は、相手の指揮官に見破られると、すぐに役に立たなくなります。そこで、置換暗号を使ってみましょう。コードを修正して、鍵を指定できるようにし、その鍵をシフトの距離として使ってみてください。
例を見てみましょう。
鍵"aaaaaaaaaaaaaaaaaa"が与えられたとき、文字列"iamapandabear"をエンコードすると、元の"iamapandabear"が返ります。
鍵"ddddddddddddddddd"が与えられたとき、文字列"iamapandabear"をエンコードすると、不明瞭化された"ldpdsdqgdehdu"が返ります。
上の例では、鍵の値としてa = 0としました。そのため、平文に鍵を足しても、同じメッセージが出てきます。つまり、"aaaa"は理想的な鍵ではありません。しかし、鍵を"dddd"にすれば、シーザー暗号と同じ結果が得られます。
どんな暗号でも、一番の弱点は人間です。乱数の源を用意し、鍵が小文字だけを含むようにすることで、置換暗号をもう少しだけ間違いに強くしましょう。
鍵がまったく指定されなかった場合は、少なくとも100文字の小文字からなる、真にランダムな鍵を生成してください。
シフト暗号は、テキストを少しだけ奇妙にすることで機能しますが、頻度分析には弱いです。置換暗号はその点を助けますが、鍵が短い場合や、スペースがそのまま残される場合は、依然として非常に弱いです。この問題の解決策の1つは、あとで"crypto-square"の演習で目にすることになります。
この分野をさらに先へ進みたくなったら、話題は、どうすれば安全に鍵を交換できるかというところへ移っていきます。この方式の最初期の実装の1つについては、WikipediaのDiffie-Hellmanを見てみましょう。
インデックスが範囲を超えてしまったときは、余りなどの算術演算を使ってインデックスを折り返してみましょう。CharacterクラスやStringクラスも頼りになります。