Vigenere Cipher Made Simple: A Step-by-Step Guide

Vigenere cipher is a polyalphabetic substitution cipher. It is a more advanced version of Caesar cipher that was developed to overcome the frequency analysis attacks.

What this guide include:

I will be discussing the history, how vigenere cipher works, the maths behind this encryption and how to break(cryptanalysis) this cipher.

A short history of this cipher:

It was developed by a French mathematician Blaise de Vigenère in the 16th century. You can think of this cipher as a series of Caesar’s shift to make it harded to break.

1) How Vigenere cipher works

This cipher uses a string of text ( a keyword) as a key which is used to do multiple alphabetic shifts on the plaintext to encrypt it to ciphertext.

The key is important in determining the number of shifts to be performed across the whole message.

Another thing to note is that the length of the key should be the same as that of the plaintext.

If that is not the case, then the chosen keyword has to be repeated several times until it has the same length as that of the message to be encoded.

After you have the key, all you have to do is shift each letter of the plaintext by the alphabetic number of the matching letter in the keyword.

This is an example of vigenere cipher:

Plaintext: CIPHERSANDCODESAREFUN

Key: KIFANGA

To encode a message use this table together with the key(Kifanga) you have decided on:

Now I take the letter that appears at the intersection of the key(letter row) and the plaintext(letter column).

So after doing this for the entire plaintext you get:

Plaintext: CIPHERSANDCODESAREFUN

Key: KIFANGAKIFANGAKIFANGA

Ciphertext: MQUHRXSKVICBJECIWESAN

To successfully decrypt a message with vigenere cipher you need both the ciphertext and the key. You just need to replace each letter that’s in the interception of the ciphertext column and key row.

Here is an example for you to practice(use “vigenere” as your key):

Ciphertext: QQMIAIIIXQVLRVZWVEKWBQV

Plaintext: ?

2) Vigenere cipher mathematics

Consider that you want to encrypt a message at letter “p”, then the alphabetic value of “p” will be equal to the plaintext and an addition to the alphabetic value that matches “p” in the key.

Vigenere cipher encryption formula is given by:

Ex = ( Mx + Kp) mod 26

Vigenere cipher decryption formula is given by:

Ex = ( Cx – Kp) mod 26

Where “E” is the code, “M” is the message, “K” is the key and “x” is the nth character of the message(determined by the length of the message) and “p” is the nth character of the key (determined by the length of the key).

3) Vigenere cipher variations

Gronsfeld cipher is very similar to vigenere cipher except that the key used is in numbers instead of letters.

Ways numbers sequence may be determined:

That’s the only difference between the two.

There is also the keyed vigenere cipher variant that is more advanced and more secure.

4) How to break (crack) Vigenere cipher

Though this cipher is more secure, it’s still vulnerable to attacks though to a lesser extent.

The most common attacks include:

  • Brute force attack, but it’s harder compared to Caesar cipher.
  • Frequency analysis attacks

There are a few online tools that you can use to further learn how vigenere cipher works.

You can encode and decode to text and English or the Language of your choice.

Tools:

6) Implementation of Vigenere cipher in various programming languages

The following are some examples of source codes that implement this cipher. Use them to learn how this encryption scheme works.

Vigenere cipher Java program

/**
 * Author: Nikko Campbell
 * StudentID: 0505046
 * File: Vigenere.java
 * 
 * A static utility class to supply functionality for operations involving the
 * Vigenere cipher. Includes encryption/decyption, frequency analysis, index of
 * coincidence calculation, and key calculation functionality.
 */
package com.nikkocampbell.vigenere;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Set;

public class Vigenere {

        /**
         * Encrypts a given plaintext message with a given keyword using the
         * Vigenere cipher
         * 
         * @param plaintext
         *            a plaintext string to be encoded
         * @param keyword
         *            a string to use in the encryption of plaintext
         * @return the encrypted version of plaintext
         */
        public static String encrypt(String plaintext, String keyword) {
                plaintext = format(plaintext);
                keyword = format(keyword);
                String ciphertext = "";

                for (int i = 0; i < plaintext.length(); i++) {
                        int plainChar = plaintext.charAt(i) - 'A';
                        int keyChar = keyword.charAt(i % keyword.length()) - 'A';
                        int cipherChar = ((plainChar + keyChar) % 26) + 'A';
                        ciphertext += (char) cipherChar;
                }

                return ciphertext;
        }

        /**
         * Decrypts a given ciphertext message encoded with a Vigenere cipher using
         * the given keyword
         * 
         * @param ciphertext
         *            a ciphertext string to be decoded
         * @param keyword
         *            a string to use in the decryption of ciphertext
         * @return the decrypted version of ciphertext
         */
        public static String decrypt(String ciphertext, String keyword) {
                ciphertext = format(ciphertext);
                keyword = format(keyword);
                String plaintext = "";

                for (int i = 0; i < ciphertext.length(); i++) {
                        int cipherChar = ciphertext.charAt(i) - 'A';
                        int keyChar = keyword.charAt(i % keyword.length()) - 'A';
                        int plainChar = ((cipherChar - keyChar) % 26);
                        if (plainChar < 0) {
                                plainChar += 26;
                        }
                        plainChar += 'A';
                        plaintext += (char) plainChar;
                }

                return plaintext;
        }

        /**
         * Calculates the number of occurrences of each letter in a given string
         * 
         * @param text
         *            a string to calculate the letter frequencies on
         * @return an integer array containing the number of occurrences of each
         *         letter within text
         */
        public static int[] letterFrequency(String text) {
                int[] frequencies = new int[26];

                text = format(text);

                for (int i = 0; i < text.length(); i++) {
                        frequencies[text.charAt(i) - 'A']++;
                }

                return frequencies;
        }

        /**
         * Wrapper function to calculate the Index of Coincidence from a given text
         * 
         * @param text
         *            a string to calculate the Index of Coincidence of
         * @return the Index of Coincidence
         * @see Vigenere#calcIC(int[])
         */
        public static double calcIC(String text) {
                return calcIC(letterFrequency(text));
        }

        /**
         * Calculates the Index of Coincidence based on the supplied letter
         * frequency array
         * 
         * @param frequencies
         *            an array containing the letter frequencies of a text
         * @return the Index of Coincidence
         * @see Vigenere#calcIC(String)
         */
        public static double calcIC(int[] frequencies) {
                double ic = 0;
                int sum = 0;
                for (int i = 0; i < frequencies.length; i++) {
                        sum += frequencies[i];
                }

                for (int i = 0; i < frequencies.length; i++) {
                        double top = frequencies[i] * (frequencies[i] - 1);
                        double bottom = sum * (sum - 1);
                        ic += top / bottom;
                }
                return ic;
        }

        /**
         * Estimates the keylength of a give string encrypted with a Vigenere cipher
         * 
         * @param text
         *            a string encrypted with a Vigenere cipher
         * @return the approximate key length of the key used to encrypt the text
         */
        public static double estimateKeyLength(String text) {
                double ic = calcIC(text);
                double top = 0.027 * text.length();
                double bottom = (text.length() - 1) * ic - 0.038 * text.length()
                                + 0.065;
                return top / bottom;
        }

        /**
         * Performs the kasiski test in order to calculate the length of the key
         * word used to encrypt a given ciphertext. Divides the text into its
         * 3-character {@link Substring}s, and counts the number of occurances of
         * each, storing each unique substring in a {@link HashMap}. After this, the
         * distances between substrings with multiple occurences are calculated, and
         * their factors are used to estimate the length of the keyword.
         * 
         * @param text
         *            a string encrypted with a Vigenere cipher
         * @param minKeyLength
         *            the minimum length that may be returned for the key length
         * @param maxKeyLength
         *            the maximum length that may be returned for the key length
         * @return the estimated length of the key used to encrypt the text
         * @see Vigenere#estimateKeyLength(HashMap, int, int)
         */
        public static int kasiski(String text, int minKeyLength, int maxKeyLength) {
                HashMap<String, Substring> substringMap = new HashMap<String, Substring>();
                text = format(text);
                String sub;

                // Fill HashMap with all substrings
                for (int i = 0; i < text.length() - 2; i++) {
                        sub = text.substring(i, i + 3);
                        if (substringMap.containsKey(sub)) {
                                substringMap.get(sub).addOccurance(i);
                        } else {
                                substringMap.put(sub, new Substring(sub, i));
                        }
                }

                // Convert HashMap to ArrayList for working with the values
                ArrayList<Substring> substrings = new ArrayList<Substring>(
                                substringMap.values());

                // Remove all substrings with only single occurrence
                substrings = Substring.removeSingleOccurrenceSubstrings(substrings);

                /*
                 * Find the differences between positions of multiple occurrences ofthe
                 * same substring and calculate the factors of each
                 */
                HashMap<Integer, Integer> factorOccurances = new HashMap<Integer, Integer>();
                for (Substring substr : substrings) {
                        ArrayList<Integer> differences = substr.getDifferences(true);
                        for (Integer diff : differences) {
                                ArrayList<Integer> factors = calculateFactors(diff);
                                for (Integer fact : factors) {
                                        if (factorOccurances.containsKey(fact)) {
                                                Integer temp = factorOccurances.get(fact);
                                                factorOccurances.put(fact, ++temp);
                                        } else {
                                                factorOccurances.put(fact, 1);
                                        }
                                }
                        }
                }

                // Analzye the frequency of all of the factors
                return estimateKeyLength(factorOccurances, minKeyLength, maxKeyLength);
        }

        /**
         * Uses a list of {@link Substring} occurences found through the Kasiski
         * test in order to estimate the length of a keyword. The most common
         * occuring factor of the distances between reoccuring substrings is most
         * likely the length of the keyword.
         * 
         * @param occurances
         * @param minKeyLength
         * @param maxKeyLength
         * @return the estimated length of the keyword for the ciphertext associated
         *         with occurrences
         * @see Vigenere#kasiski(String, int, int)
         */
        public static int estimateKeyLength(HashMap<Integer, Integer> occurances,
                        int minKeyLength, int maxKeyLength) {
                Set<Integer> keys = occurances.keySet();
                Integer maxKey = 0;
                Integer maxFreq = 0;
                for (Integer key : keys) {
                        if (key < minKeyLength)
                                continue;
                        if (key > maxKeyLength)
                                continue;
                        Integer freq = occurances.get(key);
                        if (freq >= maxFreq && key >= minKeyLength && key <= maxKeyLength) {
                                maxFreq = freq;
                                maxKey = key;
                        }
                }
                if (maxKey < minKeyLength) {
                        return minKeyLength;
                } else if (maxKey > maxKeyLength) {
                        return maxKeyLength;
                } else {
                        return maxKey;
                }
        }

        /**
         * Calculates the prime factors of a given number
         * 
         * @param num
         *            number to calculate the prime factors of
         * @return an {@link ArrayList} of Integers containing the prime factors of
         *         num
         */
        public static ArrayList<Integer> calculatePrimeFactors(int num) {
                ArrayList<Integer> factors = new ArrayList<Integer>();
                int n = num;
                for (int i = 2; i <= n / i; i++) {
                        while (n % i == 0) {
                                factors.add(i);
                                n /= i;
                        }
                }
                if (n > 1) {
                        factors.add(n);
                }
                return factors;
        }

        /**
         * Calculates the factors of a give number
         * 
         * @param num
         *            number to calculate the factors of
         * @return an {@link ArrayList} of Integers containing the factors of num
         */
        public static ArrayList<Integer> calculateFactors(int num) {
                ArrayList<Integer> factors = new ArrayList<Integer>();
                int n = num;
                for (int i = 1; i <= (int) Math.sqrt(n); i++) {
                        if (n % i == 0) {
                                factors.add(i);
                        }
                }
                int size = factors.size();
                for (int i = size - 1; i >= 0; i--) {
                        factors.add(num / factors.get(i));
                }

                return factors;
        }

        /**
         * Estimates the key used to encrypt ciphertext by splitting ciphertext into
         * a number of strings split based on keyLength and using a basic frequency
         * analysis to estimate the key used to generate them and combining to
         * estimate the key
         * 
         * @param ciphertext
         *            a string encrypted with a key of length keyLength
         * @param keyLength
         *            length of the key used to encrypt ciphertext
         * @return an estimate of the key used to encrypt ciphertext
         */
        public static String estimateKey(String ciphertext, int keyLength) {
                String separatedCipher[] = new String[keyLength];
                String key = "";

                for (int i = 0; i < keyLength; i++) {
                        separatedCipher[i] = "";
                }

                for (int i = 0; i < ciphertext.length(); i++) {
                        separatedCipher[i % keyLength] += ciphertext.charAt(i);
                }

                for (int i = 0; i < keyLength; i++) {
                        int[] freq = letterFrequency(separatedCipher[i]);
                        key += (char) ((arrayMaxPos(freq) - 4) + 'A');
                }

                return key;
        }

        /**
         * Returns the position in theArray with the highest value
         * 
         * @param theArray
         *            an int array
         * @return the position in theArray with the highest value
         */
        public static int arrayMaxPos(int[] theArray) {
                int maxPos = 0;
                for (int i = 1; i < theArray.length; i++) {
                        if (theArray[i] > theArray[maxPos]) {
                                maxPos = i;
                        }
                }
                return maxPos;
        }

        /**
         * Formats a given string for use with encryption and decryption methods.
         * Removes all non-alphabetic characters, and capitalizes all remaining
         * characters.
         * 
         * @param text
         *            a string to format
         * @return a string with proper formatting for encryption and decryption
         *         methods
         */
        public static String format(String text) {
                return text.toUpperCase().replaceAll("[^\\p{L}]", "");
        }
}

Vigenere cipher in Python

#Creates the base Alphabet which is used for finding preceeding characters from the ciphertext. baseAlphabet = ('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o',  'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z') plainText = raw_input("Please enter the plain text") key = raw_input("Please enter the key word") keyList = [] keyLength = 0 while keyLength < len(plainText):     #Adds the users entered key into a list character by character.      #Also makes the key the same length as plainText     for char in key:         if keyLength < len(plainText):             keyList.append(str(char))             keyLength = keyLength + 1 #The variable each processed letter is appended to completeCipherText = []  #This is the value used to temporaily store the ciphertext character during the iteration cipherCharIndexValue = 0 keyIncrement = 0 #iterates through the plain text for plainTextChar in plainText:         #Adds the base alphabets index value of the key and the plain text char         cipherCharIndexValue = baseAlphabet.index(keyList[keyIncrement]) + baseAlphabet.index(plainTextChar)         while cipherCharIndexValue > 25:              #makes the addition value under 26 as to not go out of range of base alphabet tuple             cipherCharIndexValue = cipherCharIndexValue - 26           #appends the ciphertext character to the completeCipherText variable.           #The character is the index of the key + index of the       plainTextChar from baseAlphabet         completeCipherText.append(baseAlphabet[cipherCharIndexValue])          #Moves onto the next key         keyIncrement = keyIncrement + 1 print ''.join(completeCipherText)#Makes the result a strings for printing to the console. 

Vigenere cipher program in c++

/*****Please include following header files*****/ // string // ctype.h /***********************************************/ /*****Please use following namespaces*****/ // std /*****************************************/ static int Mod(int a, int b) {         return (a % b + b) % b; } static string Cipher(string input, string key, bool encipher) {         int keyLen = key.size();         for (int i = 0; i < keyLen; ++i)                 if (!isalpha(key[i]))                         return ""; // Error         string output = "";         int nonAlphaCharCount = 0;         int inputLen = input.size();         for (int i = 0; i < inputLen; ++i)         {                 if (isalpha(input[i]))                 {                         bool cIsUpper = isupper(input[i]);                         char offset = cIsUpper ? 'A' : 'a';                         int keyIndex = (i - nonAlphaCharCount) % keyLen;                         int k = (cIsUpper ? toupper(key[keyIndex]) : tolower(key[keyIndex])) - offset;                         k = encipher ? k : -k;                         char ch = (char)((Mod(((input[i] + k) - offset), 26)) + offset);                         output += ch;                 }                 else                 {                         output += input[i];                         ++nonAlphaCharCount;                 }         }         return output; } static string Encipher(string input, string key) {         return Cipher(input, key, true); } static string Decipher(string input, string key) {         return Cipher(input, key, false); }

Vigenere cipher program in PHP

function Mod($a, $b) {         return ($a % $b + $b) % $b; } function Cipher($input, $key, $encipher) {         $keyLen = strlen($key);         for ($i = 0; $i < $keyLen; ++$i)                 if (!ctype_alpha($key[$i]))                         return ""; // Error         $output = "";         $nonAlphaCharCount = 0;         $inputLen = strlen($input);         for ($i = 0; $i < $inputLen; ++$i)         {                 if (ctype_alpha($input[$i]))                 {                         $cIsUpper = ctype_upper($input[$i]);                         $offset = ord($cIsUpper ? 'A' : 'a');                         $keyIndex = ($i - $nonAlphaCharCount) % $keyLen;                         $k = ord($cIsUpper ? strtoupper($key[$keyIndex]) : strtolower($key[$keyIndex])) - $offset;                         $k = $encipher ? $k : -$k;                         $ch = chr((Mod(((ord($input[$i]) + $k) - $offset), 26)) + $offset);                         $output .= $ch;                 }                 else                 {                         $output .= $input[$i];                         ++$nonAlphaCharCount;                 }         }         return $output; } function Encipher($input, $key) {         return Cipher($input, $key, true); } function Decipher($input, $key) {         return Cipher($input, $key, false); }

Vigenere cipher program in VB.Net

Private Shared Function [Mod](a As Integer, b As Integer) As Integer         Return (a Mod b + b) Mod b End Function Private Shared Function Cipher(input As String, key As String, encipher As Boolean) As String         For i As Integer = 0 To key.Length - 1                 If Not Char.IsLetter(key(i)) Then                         Return Nothing ' Error                 End If         Next         Dim output As String = String.Empty         Dim nonAlphaCharCount As Integer = 0         For i As Integer = 0 To input.Length - 1                 If Char.IsLetter(input(i)) Then                         Dim cIsUpper As Boolean = Char.IsUpper(input(i))                         Dim offset As Integer = Convert.ToInt32(If(cIsUpper, "A"c, "a"c))                         Dim keyIndex As Integer = (i - nonAlphaCharCount) Mod key.Length                         Dim k As Integer = Convert.ToInt32(If(cIsUpper, Char.ToUpper(key(keyIndex)), Char.ToLower(key(keyIndex)))) - offset                         k = If(encipher, k, -k)                         Dim ch As Char = ChrW(([Mod](((Convert.ToInt32(input(i)) + k) - offset), 26)) + offset)                         output += ch                 Else                         output += input(i)                         nonAlphaCharCount += 1                 End If         Next         Return output End Function Public Shared Function Encipher(input As String, key As String) As String         Return Cipher(input, key, True) End Function Public Shared Function Decipher(input As String, key As String) As String         Return Cipher(input, key, False) End Function

2 Comments

  1. furtdso linopv May 8, 2018
    • Chris Pete May 8, 2018

Add Comment