ヘロログ
暗号

転置式暗号(Transposition Cipher)

シーザー暗号単一換字式暗号が文字を別の文字に「置換」する換字式暗号であるのに対し、転置式暗号(Transposition Cipher)は文字の並び順を入れ替えることで情報を秘匿する暗号である。暗号文には平文と同じ文字がすべて含まれているが、その順序が変わっているため内容を読み取れない。これはアナグラム(文字の並べ替え遊び)を暗号に応用したものといえる。

スキュタレー暗号は転置式暗号の最古の例であるが、鍵空間が極めて小さいという弱点があった。転置式暗号では、ブロック内の文字の並べ替えパターン(置換規則)を自由に設定することで、鍵空間を大幅に拡大できる。

転置の技法と戦争

転置による暗号化の歴史は古く、古代スパルタのスキュタレーにまで遡る。近代においては、第一次世界大戦中にドイツ軍が使用したADFGVX暗号が有名である。これは換字と転置を組み合わせた暗号で、1918年3月にフリッツ・ネーベル(Fritz Nebel, 1891〜1977)によって考案された。フランス軍の暗号解読者ジョルジュ・パンヴァン(Georges Painvin)がこの暗号の解読に成功し、その情報はドイツ軍の春季攻勢への対策に活用された。

アルゴリズム

転置式暗号の鍵は、分割文字数n置換規則\tau(タウ)の組である。平文をn文字ずつのブロックに分割し、各ブロック内で\tauに従って文字の位置を入れ替える。

置換規則の表記

n = 4のとき、「1番目の文字を3番目に、2番目を1番目に、3番目を4番目に、4番目を2番目にする」という規則は、次の置換で表される。

\tau = \begin{pmatrix} 1 & 2 & 3 & 4 \\ 3 & 1 & 4 & 2 \end{pmatrix}

上段が元の位置、下段が移動先の位置を示す。

暗号化

平文をn文字ずつのブロックに分割し、各ブロックに置換\tauを適用する。最終ブロックがn文字に満たない場合(短ブロック)は、置換を適用せずそのまま出力する方法と、パディング(ダミー文字の追加)を行う方法がある。

復号

暗号文をn文字ずつのブロックに分割し、各ブロックに逆置換\tau^{-1}を適用する。逆置換とは、\tauによる移動を元に戻す操作である。上の例では次のようになる。

\tau^{-1} = \begin{pmatrix} 1 & 2 & 3 & 4 \\ 2 & 4 & 1 & 3 \end{pmatrix}
計算例:「cryptography」を暗号化

平文「cryptography」(12文字)をn = 4、置換\tau = \begin{pmatrix} 1 & 2 & 3 & 4 \\ 3 & 1 & 4 & 2 \end{pmatrix}で暗号化する。

12は4の倍数なので、3つのブロックに分割できる。

ブロック平文置換適用後
1c r y pR P C Y
2t o g rO R T G
3a p h yP Y A H

ブロック1を例にとると、\tauにより1番目の「c」は3番目へ、2番目の「r」は1番目へ、3番目の「y」は4番目へ、4番目の「p」は2番目へ移動する。結果は「RPCY」となる。

すべてのブロックの結果を連結すると、暗号文は「RPCYORTGPYAH」となる。

図解

転置式暗号の暗号化プロセスを図に示す。各ブロック内で置換規則\tauに従って文字の位置が入れ替わる。

平文ブロック c1r2y3p4 暗号文ブロック R1P2C3Y4 置換 τ 1→3, 2→1 3→4, 4→2 cryptography → [cryp][togr][aphy] → [RPCY][ORTG][PYAH] → RPCYORTGPYAH 図:転置式暗号の暗号化プロセス(n=4)
短ブロックの扱い

平文の長さがnの倍数でない場合、最終ブロックはn文字に満たない。この短ブロックの扱いには主に2つの方法がある。

(1) 置換を適用せずそのまま出力する。

(2) ダミー文字(パディング)を追加してn文字にしてから置換を適用する。ただしパディングに使う文字が明らかだと、置換規則の推測に利用されるおそれがある。

ナポレオンの転置式暗号

ナポレオンは様々な暗号を使ったことで知られている。1798年のエジプト遠征の際には、2文字を入れ替えるという単純な転置式暗号を使ったとされる。これはn = 2の転置式暗号であり、置換は2文字の前後を入れ替えるだけである。

\tau = \begin{pmatrix} 1 & 2 \\ 2 & 1 \end{pmatrix}
計算例:ナポレオンの転置式暗号

平文「helloworld」(10文字)をn = 2で暗号化する。

2文字ずつ区切ると「he」「ll」「ow」「or」「ld」となり、各ペアの前後を入れ替えると「EH」「LL」「WO」「RO」「DL」となる。

暗号文は「EHLLWORODL」となる。

安全性と弱点

鍵空間

転置式暗号の鍵は置換規則\tauであり、n文字のブロック内での並べ替えパターンの総数はn!nの階乗)通りである。

鍵空間の大きさ
\text{置換規則の総数} = n!
nn!
22
36
424
5120
6720
75,040
840,320

解読者がnを知らない場合、n = 2, 3, \dotsと順に調べることになる。n \leq 6と推測した場合の候補数は次のようになる。

2! + 3! + 4! + 5! + 6! = 2 + 6 + 24 + 120 + 720 = 872

単一換字式暗号の鍵空間(26! \approx 4 \times 10^{26}通り)と比べると極めて小さく、総当たり攻撃に対して脆弱である。

頻度分析への耐性

転置式暗号では文字自体は変わらないため、暗号文中の各文字の出現頻度は平文と完全に一致する。したがって、頻度分析は暗号の種類の特定(換字式か転置式かの判別)には役立つが、鍵の特定には直接使えない。解読者は頻度分布が平文の言語と一致することから転置式暗号であると見抜き、nの総当たりを行う。

換字式暗号と転置式暗号の組み合わせ

換字式暗号と転置式暗号はそれぞれ単独では弱点を持つが、両者を組み合わせることで安全性が大幅に向上する。第一次世界大戦中のADFGVX暗号はこの原理を応用した歴史的な例であり、現代のAES(Advanced Encryption Standard)でも換字(SubBytes)と転置(ShiftRows)の組み合わせが使われている。

まとめ

項目内容
分類転置式暗号(transposition cipher)
分割文字数 n と置換規則 \tau
暗号化平文を n 文字ずつ分割し、各ブロックに \tau を適用
復号暗号文を n 文字ずつ分割し、各ブロックに \tau^{-1} を適用
鍵空間n! 通り(n = 6 で720通り)
安全性低い(鍵空間が小さく総当たりが容易)
頻度分析文字頻度は不変のため、暗号種別の特定に利用される

転置式暗号は単独では安全性が低いが、文字の並べ替えという概念は暗号技術の基本要素の一つである。現代のブロック暗号(AESなど)では、換字操作と転置操作を何重にも組み合わせることで高い安全性を実現している。

実践

下のウィジェットで転置式暗号を体験できる。置換規則をカンマ区切りで入力しよう(例:3,1,4,2 は1番目→3番目、2番目→1番目、3番目→4番目、4番目→2番目を意味する)。

転置式暗号エンコーダー / デコーダー

Python実装

Pythonによる転置式暗号の暗号化・復号の実装を示す。

transposition_cipher.py
def transposition_encrypt(plaintext, tau):
    """転置式暗号で暗号化する。
    tau: 置換規則のリスト(1-indexed)。例: [3, 1, 4, 2]
    """
    n = len(tau)
    # 0-indexed の移動先マッピング
    mapping = [t - 1 for t in tau]
    cipher = []
    for i in range(0, len(plaintext), n):
        block = plaintext[i:i+n]
        if len(block) == n:
            new_block = [''] * n
            for j in range(n):
                new_block[mapping[j]] = block[j]
            cipher.append(''.join(new_block))
        else:
            cipher.append(block)  # 短ブロックはそのまま
    return ''.join(cipher).upper()


def transposition_decrypt(ciphertext, tau):
    """転置式暗号を復号する。"""
    n = len(tau)
    # 逆置換を計算
    inv = [0] * n
    for i in range(n):
        inv[tau[i] - 1] = i
    plain = []
    ct = ciphertext.lower()
    for i in range(0, len(ct), n):
        block = ct[i:i+n]
        if len(block) == n:
            new_block = [''] * n
            for j in range(n):
                new_block[inv[j]] = block[j]
            plain.append(''.join(new_block))
        else:
            plain.append(block)
    return ''.join(plain)
実行例
tau = [3, 1, 4, 2]

# 暗号化
ct = transposition_encrypt("cryptography", tau)
print(f"暗号化: cryptography -> {ct}")

# 復号
pt = transposition_decrypt(ct, tau)
print(f"復号:   {ct} -> {pt}")

# ナポレオンの転置式暗号(n=2)
ct2 = transposition_encrypt("helloworld", [2, 1])
print(f"\nナポレオンの暗号: helloworld -> {ct2}")
暗号化: cryptography -> RPCYORTGPYAH 復号: RPCYORTGPYAH -> cryptography ナポレオンの暗号: helloworld -> EHLLWORODL