fbpx

Szyfr Cezara będzie pierwszym tego typu artykułem na blogu. Do tej pory nie zajmowałem się tu aspektami związanymi z kryptografią. Oczywiście takie szyfry to bardziej ciekawostka i przyjemne wprowadzenie do prawdziwych zagadnień z tego ogromnego działu IT. 

W ramach dzisiejszego tematu odpowiemy sobie na szereg pytań:

  • Czym jest szyfr Cezara?
  • Czy zapewnia bezpieczeństwo zaszyfrowanych danych?
  • Jak wygląda implementacja w językach Python, C#, Java,  C++ oraz JavaScript?
  • Skąd wzięła się nazwa tego tytułowego szyfru?

Pytania postawione, czas przejść do przyjemnej pracy!

Zatrzymaj się!


Książki to obowiązkowa pozycja dla każdego zainteresowanego programowaniem!

Jest to zdecydowanie jedno z najlepszych źródeł do nauki programowania! Zyskasz przewagę w branży IT i osiągniesz dużo jako deweloper.


Szyfr Cezara — wprowadzenie

Zacznijmy od definicji szyfru i zapoznania się z jego historią. Szyfr Cezara jest najpopularniejszą techniką szyfrowania, a co za tym idzie, ma niemal zerowy poziom bezpieczeństwa. Wystarczy znaleźć schemat deszyfracji kilku pierwszych znaków tekstu i można uznać, że szyfr został złamany. 

Schemat szyfrowania jest banalny. Każdy znak tekstu jawnego (niezaszyfrowanego) zastępujemy inną literą, która oddalona jest o pewną liczbę znaków od litery tekstu niezaszyfrowanego. Oddalenie to nazywamy kluczem.

Dokładnie zobrazuję to na zdjęciu poniżej. Każda litera ze słowa PROGRAM została przesunięta i stworzyła ciąg znaków TVSKVEM. Posługiwałem się w tym przypadku alfabetem bez polskich znaków.

Ważne jest to, by kierunek był z góry określony. Przesuwamy zawsze do przodu lub do tyłu, jednak bardziej popularna jest ta pierwsza wersja szyfru. I taką też będziemy implementować za chwilę.

Szyfr Cezara - rysunek 1

Nazwa szyfru pochodzi od Juliusza Cezara, który szyfrował swoją korespondencję z Cyceronem. Przez to uważany jest on za najstarszy szyfr, jaki istnieje. 

Z matematycznego punktu widzenia szyfr Cezara można wyrazić za pomocą mapowania liter na cyfry, gdzie odpowiednio

A = 0

B = 1

C = 2

i tak dalej aż do 

Z = 25.

Zapis taki w matematyce jest częścią systemu liczb naturalnych zwanych arytmetyką modularną.

Szyfr Cezara — implementacja

Skupię się teraz na dokładnym opisaniu implementacji szyfru Cezara w pięciu językach — Python, Java, C#, JavaScript oraz C++. Każdy ze skryptów będzie zawierał te same dwie metody — encrypt do szyfrowania oraz decrypt do deszyfrowania. Zarówno jedna, jak i druga metoda dostaną na wejście dwie zmienne, czyli tekst do szyfrowania/odszyfrowania oraz klucz, według którego operacja szyfrowania lub odszyfrowania będzie następować.

encrypt

W metodzie encrypt deklarujemy zmienną encrypted, w której zostanie przechowana wartość zakodowanego tekstu. Następnie w pętli for następuje przejście po każdym pojedynczym znaku w tekście. Aby program był w miarę uniwersalny, musimy zapewnić mu ważny aspekt, czyli rozpoznawanie wielkości i rodzaju znaku. W przypadku dużej litery program będzie operował na innych wartościach z tabeli ASCII (więcej o ASCII we wpisie numer 1) niż w przypadku litery małej czy cyfry. Pętla będzie więc zawierać trzy warunki, na dużą literę, małą oraz właśnie cyfrę. W przypadku, gdy wstawimy dowolny inny znak, to po prostu algorytm go przepisze.

W przypadku litery dużej i małej na początku sprawdzamy wartość litery zgodnie z tabelą ASCII i odejmujemy od niej literę A. Tak otrzymany indeks następnie powiększamy o klucz oraz wykonujemy operację modulo. Po dodaniu odjętej wartości małej lub dużej litery A z tablicy ASCII otrzymamy właściwy znak przesunięty o klucz. Jest to mało intuicyjne, więc zobaczmy rysunek.

Szyfr Cezara - rysunek 2

Jak można się domyślać, stosowanie we wzorze modulo (%) jest kluczowe, ponieważ zapobiega przekroczeniu zakresu w tabeli ASCII. Jako zadanie rozpisz sobie wyznaczenie nowego znaku z litery Z. Dla bardziej ambitnych polecam obsłużenie polskich znaków.

Wracając do kodu, w przypadku cyfr stosujemy podobne przesunięcie, również posługując się wspomnianym modulo, tym razem jednak o wartości 10, ponieważ tyle posiadamy cyfr w systemie dziesiętnym. Na końcu, jeżeli przesyłamy inny znak niż litera lub cyfra to po prostu przepisujemy go bez szyfrowania.

decrypt

Funkcja deszyfrująca będzie lustrzanym odbiciem funkcji powyżej. Jedyną różnicą w kodzie będzie to, by klucz posłużył na do przesunięcia w lewo, co odwzoruje w algorytmie znak minus. Szyfr Cezara może zostać stworzony w odwrotny sposób, czyli możemy szyfrować znaki, przesuwając w lewo, natomiast deszyfrować przesuwając w prawo. To zadanie również pozostawiam Tobie drogi czytelniku. Możesz do tej implementacji rozszerzyć metody o dodatkowy, trzeci parametr, w którym przekażesz informacje, czy będziesz przesuwać w lewo, czy w prawo.

Python

def encrypt(txt, key):
    encrypted = ""
    for character in txt:
        if character.isupper():
            characterIndex = ord(character) - ord('A')
            characterShifted = (characterIndex + key) % 26 + ord('A')
            encrypted += chr(characterShifted)
        elif character.islower():
            characterIndex = ord(character) - ord('a')
            characterShifted = (characterIndex + key) % 26 + ord('a')
            encrypted += chr(characterShifted)
        elif character.isdigit():
            characterNew = (int(character) + key) % 10
            encrypted += str(characterNew)
        else:
            encrypted += character
    return encrypted


def decrypt(txt, key):
    decrypted = ""
    key = key % 26
    for character in txt:
        if character.isupper():
            characterIndex = ord(character) - ord('A')
            characterOrgPos = (characterIndex - key) % 26 + ord('A')
            decrypted +=  chr(characterOrgPos)
        elif character.islower():
            characterIndex = ord(character) - ord('a')
            characterOrgPos = (characterIndex - key) % 26 + ord('a')
            decrypted += chr(characterOrgPos)
        elif character.isdigit():
            characterOrgPos = (int(character) - key) % 10
            decrypted += str(characterOrgPos)

        else:
            decrypted += character
    return decrypted

def main(args):
    print("Ciąg zaszyfrowany:\n", encrypt("PROGRAM", 4))
    print("Ciąg odszyfrowany:\n", decrypt("TVSKVEQ", 4))
    return 0


if __name__ == '__main__':
    import sys
    sys.exit(main(sys.argv))

Java

package com.company;

public class Main {

    private static String encrypt(String txt, int key ) {
        String encrypted = "";

        for (int i = 0; i < txt.length(); i++)
        {
           if (Character.isUpperCase(txt.charAt(i)))
           {
               int characterIndex = (char)(txt.charAt(i)) - (char)('A');
               int characterShifted = (characterIndex + key) % 26 +  (char)('A');
               encrypted += (char)(characterShifted);
           }
           else if (Character.isLowerCase(txt.charAt(i)))
           {
                int characterIndex = (char)(txt.charAt(i)) - (char)('a');
                int characterShifted = (characterIndex + key) % 26 +  (char)('a');
                encrypted += (char)(characterShifted);
           }
           else if (Character.isDigit(txt.charAt(i)))
           {
               int  characterNew = ((int)(txt.charAt(i)) + key) % 10;
               encrypted += (char)(characterNew);
           }
           else
           {
               encrypted += txt.charAt(i);
           }
        }
        return encrypted;
    }

    private static String decrypt(String txt, int key ) {
        String decrypted = "";
        key = key % 26;

        for (int i = 0; i < txt.length(); i++)
        {
            if (Character.isUpperCase(txt.charAt(i)))
            {
                int characterIndex = (char)(txt.charAt(i)) - (char)('A');
                int characterOrgPos = (characterIndex - key) % 26 +  (char)('A');
                decrypted += (char)(characterOrgPos);
            }
            else if (Character.isLowerCase(txt.charAt(i)))
            {
                int characterIndex = (char)(txt.charAt(i)) - (char)('a');
                int characterOrgPos = (characterIndex - key) % 26 +  (char)('a');
                decrypted += (char)(characterOrgPos);
            }
            else if (Character.isDigit(txt.charAt(i)))
            {
                int  characterOrgPos = ((int)(txt.charAt(i)) - key) % 10;
                decrypted += (char)(characterOrgPos);
            }
            else
            {
                decrypted += txt.charAt(i);
            }
        }
        return decrypted;
    }
    public static void main(String[] args) {
        String encryptedTxt = encrypt("PROGRAM", 4);
        String decryptedTxt = decrypt("TVSKVEQ", 4);
        System.out.println(encryptedTxt);
        System.out.println(decryptedTxt);
    }
}

C#

using System;

public class Program
{	
	public static void Main()
	{
		string encryptedTxt = encrypt("PROGRAM", 4);
        string decryptedTxt = decrypt("TVSKVEQ", 4);
  		Console.WriteLine(encryptedTxt);
  		Console.WriteLine(decryptedTxt);
	}
		
    private static String encrypt(String txt, int key ) {
        String encrypted = "";

        for (int i = 0; i < txt.Length; i++)
        {
           if (Char.IsUpper(txt[i]))
           {
               int characterIndex = txt[i] - (char)('A');
               int characterShifted = (characterIndex + key) % 26 +  (char)('A');
               encrypted += (char)(characterShifted);
           }
           else if (Char.IsUpper(txt[i]))
           {
                int characterIndex = txt[i]- (char)('a');
                int characterShifted = (characterIndex + key) % 26 +  (char)('a');
                encrypted += (char)(characterShifted);
           }
           else if (Char.IsDigit(txt[i]))
           {
               int  characterNew = (int)(txt[i] + key) % 10;
               encrypted += (char)(characterNew);
           }
           else
           {
               encrypted += txt[i];
           }
        }
        return encrypted;
    }

    private static String decrypt(String txt, int key ) {
        String decrypted = "";
        key = key % 26;

        for (int i = 0; i < txt.Length; i++)
        {
             if (Char.IsUpper(txt[i]))
            {
                int characterIndex = txt[i] - (char)('A');
                int characterOrgPos = (characterIndex - key) % 26 +  (char)('A');
                decrypted += (char)(characterOrgPos);
            }
            else if (Char.IsLower(txt[i]))
            {
                int characterIndex = txt[i] - (char)('a');
                int characterOrgPos = (characterIndex - key) % 26 +  (char)('a');
                decrypted += (char)(characterOrgPos);
            }
            else if (Char.IsDigit(txt[i]))
            {
                int  characterOrgPos = (txt[i] - key) % 10;
                decrypted += (char)(characterOrgPos);
            }
            else
            {
                decrypted += txt[i];
            }
        }
        return decrypted;
    }
}

C++

#include <iostream>
using namespace std;
		
string encrypt(string txt, int key ) {
  string encrypted = "";

  for (int i = 0; i < txt.size(); i++)
  {
    if (isupper(txt[i]))
    {
      int characterIndex = txt[i] - (char)('A');
      int characterShifted = (characterIndex + key) % 26 +  (char)('A');
      encrypted += (char)(characterShifted);
    }
    else if (islower(txt[i]))
    {
      int characterIndex = txt[i]- (char)('a');
      int characterShifted = (characterIndex + key) % 26 +  (char)('a');
      encrypted += (char)(characterShifted);
    }
    else if (isdigit(txt[i]))
    {
      int  characterNew = (int)(txt[i] + key) % 10;
      encrypted += (char)(characterNew);
    }
    else
    {
      encrypted += txt[i];
    }
  }
  return encrypted;
}

string decrypt(string txt, int key ) {
  string decrypted = "";
  key = key % 26;

  for (int i = 0; i < txt.size(); i++)
  {
    if (isupper(txt[i]))
    {
      int characterIndex = txt[i] - (char)('A');
      int characterOrgPos = (characterIndex - key) % 26 +  (char)('A');
      decrypted += (char)(characterOrgPos);
    }
    else if (islower(txt[i]))
    {
      int characterIndex = txt[i] - (char)('a');
      int characterOrgPos = (characterIndex - key) % 26 +  (char)('a');
      decrypted += (char)(characterOrgPos);
    }
    else if (isdigit(txt[i]))
    {
      int  characterOrgPos = (txt[i] - key) % 10;
      decrypted += (char)(characterOrgPos);
    }
    else
    {
      decrypted += txt[i];
    }
  }
  return decrypted;
}

int main() {
  string encryptedTxt = encrypt("PROGRAM", 4);
  string decryptedTxt = decrypt("TVSKVEQ", 4);
  cout << encryptedTxt << endl;
  cout << decryptedTxt;
  return 0;
}

JavaScript

var encrypt = function(txt, key) { 
   var encrypted = ""
      
   for (var i = 0; i < txt.length; i++)
   {        
      if(txt[i] == txt[i].toUpperCase())
      {
      	 var characterIndex = txt[i].charCodeAt(0) - 'A'.charCodeAt(0);
      	 var characterShifted = (characterIndex + key) % 26 +  'A'.charCodeAt(0); 
      	 encrypted += String.fromCharCode(characterShifted);
      }
      else if(txt[i] == txt[i].toLowererCase())
      {
      	 var characterIndex = txt[i].charCodeAt(0) + 'a'.charCodeAt(0);         
      	 var characterShifted = (characterIndex - key) % 26 +  'a'.charCodeAt(0);
      	 encrypted += String.fromCharCode(characterShifted);
      }
      else if(txt[i] == txt[i].toDigit())
      {
          var characterShifted = (txt[i].charCodeAt(0) - key) % 10;
          encrypted +=  String.fromCharCode(characterShifted);
      }
      else
      {
          encrypted += txt[i]
      }
    }
	return encrypted;
};

var decrypt = function(txt, key) { 
   var decrypted = ""
   
   key = key % 26;
   
   for (var i = 0; i < txt.length; i++)
   {        
      if(txt[i] == txt[i].toUpperCase())
      {
      	 var characterIndex = txt[i].charCodeAt(0) - 'A'.charCodeAt(0);
      	 var characterOrgPos = (characterIndex - key) % 26 +  'A'.charCodeAt(0); 
      	 decrypted += String.fromCharCode(characterOrgPos);
      }
      else if(txt[i] == txt[i].toLowererCase())
      {
      	 var characterIndex = txt[i].charCodeAt(0) - 'a'.charCodeAt(0);         
      	 var characterOrgPos = (characterIndex - key) % 26 +  'a'.charCodeAt(0);
      	 decrypted += String.fromCharCode(characterOrgPos);
      }
      else if(txt[i] == txt[i].toDigit())
      {
          var characterOrgPos = (txt[i].charCodeAt(0) - key) % 10;
          decrypted +=  String.fromCharCode(characterOrgPos);
      }
      else
      {
          decrypted += txt[i]
      }
    }
	return decrypted;
};

Szyfr Cezara — podsumowanie

Bardzo się cieszę, że doszliśmy do końca i mogę pochwalić się kolejnym algorytmem na moim blogu. Myślę, że szyfr Cezara to tylko przedsmak, jaki czeka mnie podczas analizy kolejnych algorytmów kryptograficznych. Możesz zauważyć, że dziś zastosowaliśmy niewiele matematyki, a jak już to zrobiliśmy to tylko z zakresu szkoły podstawowej.

W kolejnych algorytmach z tej rodziny wejdziemy w nieco bardziej poważną matematykę, więc już na tym etapie zachęcam Cię do tego, byś dokładnie przeanalizował/a przykład dzisiejszy wraz z kilkoma małymi zadaniami, które w trakcie artykułu przemyciłem. Ich samodzielne wykonanie pozwoli Ci na lepsze zrozumienie tematu.

W przypadku problemów pisz w komentarzu poniżej. Oczywiście zachęcam Cię również do tego, by pochwalić się swoim rozwiązaniem poniżej!

Kolejny wpis przypada na podsumowanie stycznia 2020.

Newsletter

Nie przegap i dołącz już dziś do 841 osób będących w tym Newsletter! Otrzymuj co niedzielę o godzinie 20 listę kilku ciekawych tematów, które miałem okazję obserwować w mijającym tygodniu.

Tematy będą głównie techniczne, ale czasami pojawi się coś, co może wprowadzi Cię w stan zadumy i zmusi do dyskusji w szerszym gronie. Zero spamu!

Autor

Programista .NET i Python. Autor książki "Programistą być".

2 komentarze

  1. Fajny artykuł.

    W zasadzie jakby tak spojrzeć na Szyfr Ceasara to teraz o tym myślę że jest bardzo proste do zaimplementowania. Kiedyś było to coś dla mnie trudnego.

    Pisałem też na swoim blogu o tym zagadnieniu:

    1. https://mstem.net/pl/szyfr-cezara-python/

    Warto by od Szyfru Ceasara przejść do bardziej zaawansowanej wersji tego szyfru czyli takiego szyfru gdzie szyfrowanie następuje nie po kluczu ale po jakimś słowie lub zdaniu. Podejrzewam że w filmie Imitation Games o Alanie Turingu i Enigmie, sowiecki szpieg który pracował między innymi z Alanem korzystał z tego szyfru. Taki tam jest wątek.

    W zasadzie taki szyfr też nie byłby trudny do zaimplementowania. Kluczem wpierw jest różnica między pierwszą literą słowa a literą szyfrowanego tekstu, później przychodzi kolej na drugą literę i kolejną i tak dalej. Tak to sobie wstępnie wyobrażam.

    Też mam zamiar o tym napisać artykuł na swoim blogu jak znajdę czas ?.

    Dzięki Mateusz za ten artykuł i za implementację aż w 5 językach. Super sprawa ?.

    Pozdrawiam ? .

Napisz komentarz

Witryna wykorzystuje Akismet, aby ograniczyć spam. Dowiedz się więcej jak przetwarzane są dane komentarzy.

SPRAWDŹ POLECANĄ KSIĄŻKĘ. Najlepsze materiały do nauki programowania!

X