Skip to content
BetaDE
Lesson 2 of 5Exploring computer science

Exploring computer science · Lesson 2 · 45 minutes

Secret codes – Caesar and Vigenère

What you'll learn

  • Understand and program the Caesar cipher
  • See why Caesar is easy to crack
  • Encrypt more securely with a keyword using Vigenère

Secret messages

More than 2000 years ago, people already wanted to write messages that strangers couldn’t read. The Roman general Julius Caesar is said to have used a simple trick for this. Today your phone and your browser encrypt all the time – with much stronger methods, but with the same basic idea.

The Caesar cipher

Every letter is shifted by a fixed number in the alphabet. With a shift of 3, A becomes D, B becomes E – and after Z it starts again at A. % 26 takes care of the “start again”:

def caesar(text, shift):
    result = ""
    for character in text:
        if "A" <= character <= "Z":
            number = ord(character) - ord("A")       # A = 0, B = 1, …
            number = (number + shift) % 26
            result = result + chr(number + ord("A"))
        else:
            result = result + character
    return result


secret = caesar("MEET AT EIGHT", 3)
print(secret)
print(caesar(secret, -3))

You decrypt with the same function: just shift back by -3.

Cracking Caesar

There are only 25 useful shifts. A computer tries them all in a fraction of a second. That’s called brute force. Can you find the message?

def caesar(text, shift):
    result = ""
    for character in text:
        if "A" <= character <= "Z":
            result = result + chr((ord(character) - ord("A") + shift) % 26 + ord("A"))
        else:
            result = result + character
    return result


secret = "XLI XVIEWYVI MW FILMRH XLI KCQ"
for attempt in range(1, 26):
    print(attempt, caesar(secret, -attempt))

One line makes sense – so Caesar is not secure.

Vigenère: with a keyword

In the 16th century, the Frenchman Blaise de Vigenère had a better idea: every letter is shifted by a different amount. A keyword says how far. With the key KEY, the first letter is shifted by K (10), the second by E (4), the third by Y (24) – and then it starts again with K:

TextHELLO
KeyKEYKE
Shift10424104
SecretRIJVS

The two L’s now become different letters (J and V). That makes cracking much harder. With the remainder operator % you repeat the key again and again:

key = "KEY"
for number in range(7):
    print(number, key[number % len(key)])

Good to know: Vigenère is cracked today too – with statistics about frequent letters. Modern encryption like AES calculates with bits instead of letters and is so strong that not even all the computers in the world together could crack it with brute force.

Your task

Your turn

Write vigenere(text, key, direction). The text consists of capital letters, spaces and punctuation. Every letter is shifted by the value of the matching key letter (A = 0, B = 1, … Z = 25). The key repeats – but only letters use up a key letter. All other characters stay as they are. direction is 1 to encrypt and -1 to decrypt.

def vigenere(text, key, direction):
    result = ""
    number = 0  # which key letter is next?
    for character in text:
        result = result + character
    return result


secret = vigenere("HELLO WORLD", "KEY", 1)
print(secret)
print(vigenere(secret, "KEY", -1))

Solve it without hints to earn a ⭐ on your progress page.

Show solution

Try it yourself first – mistakes teach you the most. Here is one possible solution:

def vigenere(text, key, direction):
    result = ""
    number = 0  # which key letter is next?
    for character in text:
        if "A" <= character <= "Z":
            shift = ord(key[number % len(key)]) - ord("A")
            new = (ord(character) - ord("A") + direction * shift) % 26
            result = result + chr(new + ord("A"))
            number = number + 1
        else:
            result = result + character
    return result


secret = vigenere("HELLO WORLD", "KEY", 1)
print(secret)
print(vigenere(secret, "KEY", -1))