エニグマ暗号に挑みたい①

エフ
2026-04-22
2026-04-22

表紙

我々はコンピュータを扱う仕事をしております。

そのためにコンピュータのルーツを深く知る必要があります。

コンピュータのルーツといえば諸説ありますが、やはりアラン・チューリングが実施したエニグマ暗号の解読であります。

というわけで、コンピュータをより深く知るために
今回はエニグマ暗号を解読したい!エニグマ暗号に挑みます!


ではまずエニグマ暗号とはどういったものか、
そこから行きましょう。

エニグマ暗号は、大戦中にドイツ軍が使用した電気機械式の暗号装置で、当時としては非常に高度な暗号化技術を備えていたらしいです。

1-Mar-10-2026-07-53-16-9547-AM

エニグマ暗号の特徴

構造
エニグマはタイプライターのようなキーボードを持ち、入力された文字を複雑な電気回路と回転するローターによって暗号化します。
主な構成要素は以下の通り:

ローター(回転子):複数枚あり、入力信号を複雑に変換。
反射板:信号を逆方向に戻すことで暗号化の複雑さを倍増。
プラグボード:文字の入れ替えを追加し、さらに暗号強度を高める。

暗号化の仕組み
キーを押すたびにローターが回転し、配線パターンが変化するため、同じ文字でも毎回異なる暗号文字になります。
この動的な変化が解読を非常に困難にしました。


ふむふむ、なんとなく概要は理解しました。
それでは実際に暗号解読に挑むために、まずはエニグマを再現しましょう。

いやしかし、実際と同じ仕組みを再現をするには、できないこともなさそうですが、少し複雑な気もします。
というわけで簡略化いたします。

ちょっといまいちイメージのつかめない反射板とか、プラグボードといった機能を除外しましょう。


ということで、ローター(回転子)のみを使う感じにします。

2-Mar-10-2026-07-53-26-6177-AM

回転子は3つです。例えばAを暗号化しようとした場合には①→②→③にそれぞれ対応した文字に変換され出力されます。
例> A→①→H→②→W→③→Q  みたいな感じです。
A→C みたいな1対1に変換されるわけではないので複雑になります。

またより複雑にするために、それぞれの回転子もそれぞれのタイミングで回転します。
回転することで、同じAという文字を変換した場合でも、タイミングによって違う文字に変換されます。
①は1文字打つごとに1文字回転、②は①が1回転(26文字)するごとに1文字回転、③は②が1回転(26文字)するごとに1文字回転します。

回転子には①②③それぞれの配置がきまっており、
これはどの暗号機でも共通とします。
実際のエニグマでもこの部分は共通だったらしいです。
※おそらく実際のエニグマでは配線パターンということになると思います。

そして、この①②③の回転子について、
初期配置からどの程度ずらすか、を数値化します。
アルファベットの1~26までです。

① 1~26  ② 1~26  ③ 1~26

というそれぞれの3つの数字が暗号化における共通鍵となります。


これをそれぞれの暗号機で同じ数値を設定することで、
離れた位置同士でエニグマを使った暗号化→復号化を可能にします。


それではいよいよ簡易エニグマコードをPythonで作成です。
※もはや当たり前のようにAIと相談しながら作成してます。

-----------------------------------------------------------------------
import string

letters = list(string.ascii_uppercase)

# 固定ローター(配値)
rotor1 = list("EKMFLGDQVZNTOWYHXUSPAIBRCJ")
rotor2 = list("AJDKSIRUXBLHWTMCQGZNPYFVOE")
rotor3 = list("BDFHJLCPRTXVZNYEIWGAKMUSQO")

def rotate(rotor):
    return rotor[1:] + rotor[:1]

def encrypt(message, rotors, offsets):
    r1, r2, r3 = rotors
    o1, o2, o3 = offsets
    encrypted = []
    step1, step2 = 0, 0

    for ch in message.upper():
        if ch not in letters:
            encrypted.append(ch)
            continue

        idx = (letters.index(ch) + o1) % 26
        ch = r1[idx]
        idx = (letters.index(ch) + o2) % 26
        ch = r2[idx]
        idx = (letters.index(ch) + o3) % 26
        ch = r3[idx]
        encrypted.append(ch)

        # カスケード回転
        r1 = rotate(r1)
        step1 += 1
        if step1 % 26 == 0:
            r2 = rotate(r2)
            step2 += 1
            if step2 % 26 == 0:
                r3 = rotate(r3)

    return ''.join(encrypted)

def decrypt(cipher, rotors, offsets):
    r1, r2, r3 = rotors
    o1, o2, o3 = offsets
    decrypted = []
    step1, step2 = 0, 0

    for ch in cipher.upper():
        if ch not in letters:
            decrypted.append(ch)
            continue

        idx = r3.index(ch)
        ch = letters[(idx - o3) % 26]
        idx = r2.index(ch)
        ch = letters[(idx - o2) % 26]
        idx = r1.index(ch)
        ch = letters[(idx - o1) % 26]
        decrypted.append(ch)

        # カスケード回転
        r1 = rotate(r1)
        step1 += 1
        if step1 % 26 == 0:
            r2 = rotate(r2)
            step2 += 1
            if step2 % 26 == 0:
                r3 = rotate(r3)

    return ''.join(decrypted)

# ===== 実行部分 =====
mode = input("モードを選択してください(encrypt / decrypt): ").strip().lower()
text = input("メッセージを入力してください: ")
key_input = input("キー(例: 5 12 3)を入力してください: ")
offsets = list(map(int, key_input.split()))

rotors = [rotor1[:], rotor2[:], rotor3[:]]

if mode == "encrypt":
    result = encrypt(text, rotors, offsets)
    print("暗号化結果:", result)
elif mode == "decrypt":
    result = decrypt(text, rotors, offsets)
    print("復号結果:", result)
else:
    print("モードが不正です。encrypt または decrypt を指定してください。")
-------------------------------------------------------------------

ということでできました!

ちょろっと解説。この部分が各回転子の配置ですね。
-----------------------------------
# 固定ローター
rotor1 = list("EKMFLGDQVZNTOWYHXUSPAIBRCJ")
rotor2 = list("AJDKSIRUXBLHWTMCQGZNPYFVOE")
rotor3 = list("BDFHJLCPRTXVZNYEIWGAKMUSQO")
-----------------------------------

こちらは各回転子の回転の制御ですね
-----------------------------------
        # カスケード回転
        r1 = rotate(r1)
        step1 += 1
        if step1 % 26 == 0:
            r2 = rotate(r2)
            step2 += 1
            if step2 % 26 == 0:
                r3 = rotate(r3)
-----------------------------------

このコードを実行すると、暗号化(encrypt)か、復号化(decrypt)か 聞いてきます。
どちらのモードでも、次にメッセージを入力します。
最後に、共通鍵の入力です。


というわけで実際に実行します。

--------------------------------------------------
モードを選択してください(encrypt / decrypt): encrypt
メッセージを入力してください: WAREWAREHAENIGUMAANNGOUNIIDOMITAI(我々はエニグマ暗号に挑みたい)
キー(例: 5 12 3)を入力してください: 2 22 15
暗号化結果: HQPNZVSYLWFEXPIQDUVMZWDBMNOMJFCRJ
--------------------------------------------------

出ました、暗号結果
「HQPNZVSYLWFEXPIQDUVMZWDBMNOMJFCRJ」

後はこの暗号を打電します。
これを受け取った側には事前に共通鍵が知らされています。
※今回は 「2 22 15」 実際には日付によって毎日共通鍵が変わるとか事前に取り決めておきます。

ということで復号。
--------------------------------------------------
モードを選択してください(encrypt / decrypt): decrypt
メッセージを入力してください: HQPNZVSYLWFEXPIQDUVMZWDBMNOMJFCRJ
キー(例: 5 12 3)を入力してください: 2 22 15
復号結果: WAREWAREHAENIGUMAANNGOUNIIDOMITAI
--------------------------------------------------

復号できました!
「WAREWAREHAENIGUMAANNGOUNIIDOMITAI」(我々はエニグマ暗号に挑みたい)

昨今はリモート環境でのチャットやり取りが多いかと思いますが、
平文でやり取りしていると、上司に盗み見られて困ります。
このコードを同僚に配布し、共通鍵を決め、暗号化した内容をチャットに流すようにしましょう。

いや、話がそれましたが、そもそもの目的はエニグマに挑むことです。
次回はこの簡易エニグマに挑み、暗号文を解読します!