Szyfrowanie symetryczne

Szyfry towarzyszą ludzkości już od dawien dawna. Przez wieki powstała ich cała masa. Jednym było bliżej, a innym dalej do doskonałości. Jednak rzeczą, która je łączy jest chęć przekazywania poufnych informacji w taki sposób, aby odczytać mogła je tylko osoba, dla której ta informacja jest przeznaczona. Dobre szyfrowanie ważnych informacji mogło często decydować np. o przegranej bitwie lub wojnie. Jednak jakie zastosowanie ma szyfrowanie w naszym obecnym codziennym życiu? Zastosowań jest cała masa. Szyfrowane są m.in. połączenia zestawiane z serwerami banków. Gdyby dane dot. naszych operacji finansowych były przesyłane, bez uprzedniego szyfrowania to byłoby to proszenie się o kradzież naszych pieniędzy. Dzięki szyfrowaniu możemy też zabezpieczyć dane, jakie przechowujemy na naszym komputerze – bez znajomości hasła nikt nie uzyska do nich dostępu.

Szyfrowanie symetryczne

Na wstępie należy zaznaczyć, że istnieją dwa rodzaje szyfrowania. Są to: szyfrowanie symetryczne i asymetryczne. Ja w tym wpisie opiszę zasadę działania tylko tego pierwszego. Na czym, więc polega szyfrowanie symetryczne? Jego głównym założeniem jest to, że informację szyfruje i deszyfruje się za pomocą tego samego klucza. Mówiąc inaczej: Aby rozszyfrować wiadomość zaszyfrowaną tą metodą musimy znać klucz, który został użyty do zaszyfrowania tej wiadomości. Klucz, więc jest rzeczą, która jest tajna i powinna być z tego powodu trzymana w tajemnicy. Jeżeli ktoś pozna klucz, nie będzie miał żadnego problemu z odszyfrowaniem naszych danych. Z tego, też powodu szyfrowanie te nie może być stosowane do zabezpieczania połączeń ze stronami WWW. Dlaczego? Dlatego, że z kapelusza klucza użytego do szyfrowania strony sobie nie wyczarujemy, a jego wymiana w sposób jawny jest bardzo ryzykowna – może zostać przechwycony. Wtedy szyfrowanie traci jakikolwiek sens. Właśnie z tego powodu powstało szyfrowanie asymetryczne. Jednak nie o nim jest ten wpis 😉

Reasumując: Szyfrowania symetrycznego używamy, aby zaszyfrować jakieś dane przy pomocy wybranego klucza(hasła) i móc je zdeszyfrować, również przy pomocy tego samego klucza(hasła).

Podstawowa terminologia związana z szyfrowaniem

Zanim przejdziemy dalej warto, żebyś poznał kilka podstawowych pojęć, którymi będę posługiwał się w dalszej części wpisu.

Dane

Czyli po prostu to co będziemy szyfrować 😉

Szyfrogram

Mówiąc prosto: Zaszyfrowane dane.

Hasło

Jest to ciąg znaków, którego używamy do szyfrowania i deszyfrowania danych. Musi być tajny.

Klucz szyfrujący

Można powiedzieć, że jest to skrót generowany z naszego hasła(oraz soli), który ma stałą długość wyrażoną w bitach. Klucze mogą być 64,128,256 i więcej – bitowe. To właśnie klucz szyfrujący (a nie hasło!) bierze bezpośredni udział w szyfrowaniu danych. Generalnie rzecz biorąc im klucz jest dłuższy tym lepiej. Jednak nie każdy algorytm szyfrujący obsługuje wszystkie długości kluczy. Klucz również powinien być tajny.

Sól

Jest to ciąg bitów, który również bierze udział w szyfrowaniu danych. W zasadzie są to dane dopisywane do klucza szyfrującego. Sól jest jawna, jednak jej wartość powinna być za każdym razem inna, kiedy szyfrujemy dane – czyli losowa. Zapobiega to sytuacji, w której używając takiego samego hasła i takich samych danych otrzymamy taki sam szyfrogram. Prościej mówiąc: Szyfrując wiadomość „ala ma kota” hasłem „123” nie używając soli zawsze otrzymamy wyjściowy wynik (przykładowo) „#@##$df48!!”. To znacząco ułatwia łamanie szyfru. Jednak używając losowej jawnej soli sprawimy, że wynikowy szyfrogram nigdy nie będzie taki sam. Należy przy tym dodać, że znajomość wartości soli jest niezbędna do deszyfracji danych. Można ją jednak trzymać np. w pliku razem z szyfrogramem.

Wektor inicjujący (IV)

Jest to ciąg bitów o stałej długości. Pełni właściwie taką samą funkcję co sól, z tą różnicą, że jest on używany jako dodatkowe dane wejściowe dla funkcji szyfrującej. Powinien być też losowy.

Dlaczego powinniśmy korzystać z istniejących algorytmów szyfrujących?

Pisząc swoją własną funkcję szyfrującą, trzeba być świadomym, że prawie na pewno będzie ona niedoskonała. Informacja, że ktoś ją złamał może do nas nigdy nie dotrzeć lub dotrzeć zbyt późno – kiedy dane użytkowników nowego algorytmu szyfrującego zostały dawno wykradzione. Z kolei używanie gotowych i znanych rozwiązań(jak choćby AES), których używa cała masa innych osób daje nam tę przewagę, że w przypadku jego złamania, taka informacja obiegnie szybko świat i będzie można w rozsądnym czasie powziąć jakieś kroki mające na celu ochronę naszych danych. Pomijając już fakt, że prawdopodobieństwo złamania takiego szyfru jest dużo mniejsze niż napisanego przez amatora.

Implementacja szyfrowania symetrycznego w C#

Wydaje mi się, że chyba tyle teorii wystarczy 😉 Przejdźmy, więc w końcu do kodu. Przedstawiam Ci prostą klasę umożliwiającą szyfrowanie ciągów znaków. Opiera się ona na algorytmie AES, który jest zaimplementowany już w .NET:

Trochę tego jest… Przejdźmy, więc do opisu wszystkich metod:

  • EncryptString(string input, byte[] key, byte[] IV) – Metoda ta zwraca zaszyfrowane dane w postaci stringa. Jako argumenty przyjmuje, kolejno: dane wejściowe, czyli to co chcemy zaszyfrować; klucz szyfrujący, czyli istny mix naszego hasła i soli; oraz Wektor inicjujący.
  • DecryptString(string input, byte[] key, byte[] IV) – Metoda zwracająca odszyfrowane dane w postaci stringa. Jako argumenty przyjmuje takie same dane, jak funkcja szyfrująca z tą różnicą, że dane wejściowe to szyfrogram. Aby metoda odszyfrowała dane, należy użyć takiego samego klucza szyfrującego co w metodzie szyfrującej, oraz tego samego IV.
  • GenerateSalt() – Metoda zwraca tablicę bajtów, służy do generowania losowej soli.
  • GenerateIV() – Metoda podobna do powyższej. Generuje IV o odpowiedniej długości.
  • CreateKey(string InputKey, byte[] saltBytes) – Metoda zwraca tablicę bajtów, czyli klucz, którym szyfrowane będą dane. Klucz jest obliczany na podstawie InputKey – czyli hasła, które jest ciągiem znaków, oraz losowej soli.
  • GetHMAC(byte[] hashdata, byte[] key) – Metoda, która oblicza HMAC dla danych wejściowych, jakimi są dane już zaszyfrowane i właściwy klucz szyfrujący. To umożliwia tzw. autoryzację hasła. Jednak o tym napiszę dalej.

Używanie klasy w praktyce

Jak używać powyższej klasy w praktyce? To dosyć proste:

Za każdym razem, kiedy uruchomimy program szyfrogram będzie wyglądał inaczej. Dlaczego? Właśnie dlatego, że używamy do szyfrowania losowej soli i IV.

UWAGA! Jeżeli chcesz zapisać zaszyfrowane dane do pliku celem ich późniejszego odczytania i odszyfrowania, musisz zapisać tam również IV i Sól – które mają zostać użyte jako argumenty dla funkcji DecryptString(), oraz CreateKey().

Autoryzacja hasła

Ostatnią rzeczą, jaką tutaj opiszę, będzie autoryzacja wprowadzanego hasła. Innymi słowy sprawdzanie czy hasło, które zostało wpisane jest faktycznie tym, które zostało użyte do szyfrowania. Co prawda podanie błędnego hasła w większości przypadków spowoduje wyjątek, więc kod funkcji DecryptString() można po prostu zamknąć w blok try i mieć spokój – kiedy wystąpi wyjątek wywalamy komunikat, że hasło jest błędne. Jednakże jest to złe podejście. Dlaczego? Po pierwsze nie zawsze deszyfrowanie musi się zakończyć błędem, wtedy w wyniku deszyfracji otrzymamy jakieś „krzaczki”. Po drugie takie podejście umożliwia modyfikację na zaszyfrowanych danych, jakie przechowujemy w pliku, co ułatwia atak na nasz szyfrogram. Jak temu zaradzić? Odpowiedź brzmi: używając HMAC’a. Czym jest? To taka funkcja hashująca, ale używająca do hashowania nie tylko suchych danych, ale też klucza potrzebnego do szyfrowania. Jak używać tego w praktyce?

  1. Po zaszyfrowaniu danych obliczamy HMAC szyfrogramu, używając do tego prawidłowego klucza szyfrującego. Warto uwzględnić tutaj też sól i IV, jeżeli przechowujemy ją również w pliku z zaszyfrowanymi plikami.
  2. HMAC zapisujemy na końcu naszego pliku.
  3. Podczas otwierania pliku i podaniu przez użytkownika hasła, obliczamy HMAC na podstawie podanego przez niego hasła i danych obecnych w pliku. Następnie porównujemy HMAC jaki został na tej podstawie obliczony, z tym który obecny jest na końcu pliku. Jeżeli obydwie wartości są identyczne – oznacza to, że zarówno hasło, jak i dane do siebie pasują. Jeżeli wartości są różne – użytkownik podał złe hasło lub szyfrogram został zmodyfikowany.

Dla zobrazowania, garść kodu:

Zmiana hasła, którego użyjemy do odszyfrowania danych: string userPassword = „tajne!”; spowoduje wyświetlenie błędu Console.WriteLine(„Haslo jest nieprawidlowe, lub dane sa uszkodzone!”);

Dlaczego? Ponieważ wyliczony przy szyfrowaniu HMAC, nie zgadza się z tym wyliczonym przed przystąpieniem do deszyfrowania. W przypadku podania hasła prawidłowego – program deszyfruje wiadomość.

Myślę, że to już wszystko z podstaw szyfrowana symetrycznego. Jeżeli gdzieś popełniłem błąd lub czegoś nie dopowiedziałem piszcie w komentarzach, jeżeli macie jakieś problemy/pytania to też piszcie – chętnie pomogę, w miarę moich możliwości.

Jeżeli podobają Ci się moje wpisy to jak zwykle zachęcam do polubienia fanpage bloga na facebooku 😉

 

922 total views, 1 views today

4 przemyślenia nt. „Szyfrowanie symetryczne

    • Dzięki 😉

      IV to skrót oznaczający wektor inicjujący. Są to dane, których algorytm szyfrujący używa jako dodatkowych danych wejściowych (oprócz danych, które będą szyfrowane), w celu nadania szyfrogramowi niepowtarzalności.

    • Szyfrowaniem asymetrycznym jeszcze w C# się nie zajmowałem, więc na pewno nie w najbliższej przyszłości. Jednak mam w planach napisanie prostego chatu tekstowego, przy okazji więc zajmę się też szyfrowaniem asymetrycznym. Jak ogarnę temat na pewno zamieszczę o tym wpis na blogu, wraz z kodem 😉

      Pozdrawiam!

Możliwość komentowania jest wyłączona.