#45 Caesar Cipher
来週資格の試験があります。コツコツ勉強してきましたが、いつだって試験は不安です。あと一週間集中していきます。
それはさておき、CTFをやっていると必ず暗号の問題がでてきます。なんとなく苦手意識があったので、基礎から固めていこうと思っています。とりあえず、有名なシーザー暗号から。
目標
シーザー暗号で暗号化・復号を行うプログラムを書いてみます。
作業
シーザー暗号について
シーザー暗号とは、かの有名な古代ローマのジュリアス・シーザーが使ったと言われる暗号です。仕組みは非常に単純で、平文を1文字ずつ、アルファベットの辞書順にずらすことで暗号化します。例えば、「ABC」の各文字を3文字ずつずらすと、「DEF」になりますね。同様に、「Caesar」は、「Fdhvdu」のような文字列になり、一見なにやらわかりません。復号するときは、同じ数だけ逆方向にずらせばよいので、単純です。
このとき、ずらした幅である「3」は暗号鍵と呼べるでしょう。この鍵さえ共有していれば、秘密のメッセージをやりとりできます。逆にいえば、鍵が漏れてしまうと、メッセージも簡単に復号できてしまいます。
プログラムの作成
仕組みがわかったところで、プログラムを組んでみましょう。Pythonです。
class Caesar():
#文字の一覧。この中に含まれる文字のみ扱える
ALPHABET = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789 -_=+[]{};:'\"\\|,.<>/?!@#$%^&*()"
def __init__(self, key):
# 暗号鍵を設定する
self.key = key
# 暗号化する
def encrypt(self, message, key=None):
result = self.translate(message, key, decrypt=False)
return result
# 復号する
def decrypt(self, message, key=None):
result = self.translate(message, key, decrypt=True)
return result
# シーザー暗号で文字列を変換する
def translate(self, message, key=None, decrypt=False):
if key == None:
key = self.key
result = ""
for char in message:
original = self.ALPHABET.find(char)
# 文字が一覧にないときはスキップ
if original < 0:
print("[!] Cannot process '" + char + "'. Skip.")
continue
# 変換後の文字の位置
mod = (key % len(self.ALPHABET))
if decrypt:
index = original - mod
if index < 0:
index += len(self.ALPHABET)
else:
index = original + mod
if index > len(self.ALPHABET):
index -= len(self.ALPHABET)
result += self.ALPHABET[index]
return result
試してみます。
if __name__ == "__main__":
caesar = Caesar(3)
message = "test"
encrypted = caesar.encrypt(message)
print("暗号文: " + encrypted)
decrypted = caesar.decrypt(encrypted)
print("復号: " + decrypted)
$ python3 caesar.py
暗号文: whvw
復号: test
ちゃんと暗号化・復号できました。!
弱点
ご覧のように、シーザー暗号は単純な仕組みなので、簡単に破れてしまいます。まず、暗号鍵の種類が少ないのは問題です。今回作成したプログラムでは、アルファベットの大文字・小文字と数字、記号を扱っていて全部で93あります。93文字ずらしたらもとの文字に戻ってきてしまうので、使える暗号鍵は、93パターンということになります。全暗号鍵を試すのは容易いことです。
先程のプログラムの"Caesar"クラスにメソッドを追加します。
# 全パターン試す
def bruteforce(self, message):
result = []
for i in range(len(self.ALPHABET)):
decrypted = self.decrypt(message, key=i)
result.append((i, decrypted))
return result
このメソッドで、暗号文「1[].D].D._ ,_<Y」を解読してみると…
if __name__ == "__main__":
caesar = Caesar(0)
encrypted = "1[].D].D._ ,_<Y"
result = caesar.bruteforce(encrypted)
for i in result:
print(str(i[0]) + ": " + i[1])
$ python3 caesar.py
0: 1[].D].D._ ,_<Y
1: 0+[,C[,C,-9|-.X
2: z=+|B+|B| 8\ ,W
3: y_=\A=\A\97"9|V
...
31: Wklv=lv=vhfuhw$
32: Vjku_ku_ugetgv#
33: Uijt-jt-tfdsfu@
34: This is secret!
35: Sghr9hr9rdbqds?
36: Rfgq8gq8qcapcr/
...
暗号鍵34のときに、それっぽい文章ができています!このように、総当りで復号できてしまうとなると、大事な情報は扱いたくありませんね…
まとめ
シーザー暗号のプログラムを作ってみました。現代ではあまり使われていない古典暗号のひとつですが、暗号の入口としては面白いものかなと思います。現代のインターネットを支える、公開鍵暗号のような複雑な技術にも踏み込んでいけるよう、少しずつ勉強していきます。
EOF