Шифр Виженера

Шифр Виженера триста лет считали невзламываемым. Его секрет — переменный сдвиг, заданный ключевым словом.
Современники называли его le chiffre indéchiffrable — «нераскрываемый шифр».

Идея: много сдвигов вместо одного

Слабость Цезаря в том, что сдвиг одинаков для всех букв. Виженер исправляет это: сдвиг меняется от буквы к букве по ключевому слову. Например, ключ KEY задаёт сдвиги K(10), E(4), Y(24), которые повторяются по кругу для всего сообщения.

Как это работает на примере

ТекстHELLO
КлючKEYKE
Сдвиг10424104

Одна и та же буква (две L) теперь шифруется по-разному, потому что для них разные сдвиги. Это разрушает простой частотный анализ.

Шифруем по Виженеру

import string
alphabet = string.ascii_uppercase

def vigenere_encrypt(text, key):
    result = ""
    key = key.upper()
    j = 0
    for ch in text.upper():
        if ch in alphabet:
            shift = alphabet.index(key[j % len(key)])
            result += alphabet[(alphabet.index(ch) + shift) % 26]
            j += 1
        else:
            result += ch
    return result

print(vigenere_encrypt("HELLO WORLD", "KEY"))

Вывод:

RIJVS UYVJN

Расшифровка

Чтобы расшифровать, вычитаем сдвиг ключа вместо прибавления:

import string
alphabet = string.ascii_uppercase

def vigenere(text, key, mode=1):
    result, j, key = "", 0, key.upper()
    for ch in text.upper():
        if ch in alphabet:
            shift = alphabet.index(key[j % len(key)]) * mode
            result += alphabet[(alphabet.index(ch) + shift) % 26]
            j += 1
        else:
            result += ch
    return result

secret = vigenere("ATTACK AT DAWN", "LEMON", 1)
print("Зашифровано:", secret)
print("Расшифровано:", vigenere(secret, "LEMON", -1))

Вывод:

Зашифровано: LXFOPV EF RNHR
Расшифровано: ATTACK AT DAWN

В чём всё-таки слабость

Виженер держался долго, но в XIX веке его взломали. Хитрость в том, что ключ повторяется. Если угадать длину ключа (например, 3), то можно разбить шифр-текст на 3 группы, в каждой из которых сдвиг постоянный — а значит, к каждой группе снова применим частотный анализ! Метод поиска длины ключа называется тестом Касиски.

cipher = "RIJVSUYVJN"
key_len = 3
for start in range(key_len):
    group = cipher[start::key_len]
    print("Группа", start, ":", group)

Вывод:

Группа 0 : RVYN
Группа 1 : IS V
Группа 2 : JUJ

Каждая группа зашифрована одним сдвигом — значит, это снова обычный Цезарь, который мы уже умеем ломать. Вывод урока: даже умный классический шифр в итоге сдаётся статистике. Настоящую стойкость дают только современные алгоритмы.

Проверьте себя
1. Чем шифр Виженера принципиально отличается от шифра Цезаря?
AОн не использует ключ
BСдвиг меняется от буквы к букве по ключевому слову
CОн работает только с цифрами
DОн необратим
2. Как взламывают шифр Виженера?
AПеребирают все 25 сдвигов
BНаходят длину ключа, делят текст на группы и применяют частотный анализ к каждой
CИспользуют Base64
DЕго невозможно взломать
Поддержать проект