Bezpieczeństwo w aplikacjach C#. Podstawy i nie tylko. Część II.

Kontynuując temat bezpieczeństwa, chce zająć się tematem certyfikatów ich tworzeniem i obsługą. Omówię co to jest PKI, CA, X.509.

To o czym nie wspomniałem w ostatnim poście a może przydać się w tym poście to wiedza o tym, że algorytm asymetryczny z kluczem publicznym i prywatnym ma możliwość szyfrowania danych przy pomocy klucza publicznego jak i prywatnego. Gdy zaszyfrujemy dane kluczem prywatnym, będziemy mieć możliwość deszyfracji wiadomości używając klucza publicznego. Gdy użyjemy klucza publicznego do szyfrowania danych, odszyfrowanie będzie możliwe tylko przy użyciu klucza prywatnego.

Problem

Wracają do tematu Alice i Bob-a. W poprzednim poście udało się nam wymienić wiadomości pomiędzy Bob-em i Alice. Komunikacja była zabezpieczona poprzez algorytm asymetryczny, który służył do wymiany klucza do algorytmu symetrycznego.

Do rozwiązania pozostał Nam tylko jeden problem. Skąd Bob ma pewność, że wiadomość, klucz publiczny, który otrzymał od Alice należy do Alice a nie do Oscara?

Jednym zagrożeniem przy zastosowaniu szyfrowania asymetrycznego jest możliwość zastąpienia klucza publicznego Alice kluczem publicznym Oscara.

Bob w tej sytuacji może zaszyfrować korespondencję niewłaściwym kluczem publicznym, przez co umożliwi odczytanie wiadomości Oscarowi. Oscar odczyta wiadomość i zaszyfruje ją kluczem publicznym Alice i wyśle do Alice. Alice odszyfruje wiadomość myśląc, że pochodzi o Bob-a.

Certyfikacja

Dlatego istnieje mechanizm procesu certyfikacji klucza, który potwierdza jego autentyczność i prawo do korzystania przez Alice. Ogólnie o systemach dostarczających infrastruktorę potrzebna do obsługi mechanizmów wymiany klucza publicznego mówimy (PKI) czyli infrastrukturze klucza publicznego.

Certyfikat X.509

Fizycznie jest to ciąg danych. Najpopularniejszym standardem certyfikatów jest X.509. X.509 składa się z pól:

  • wersja,
  • numer seryjny,
  • wydawca certyfikatu,
  •  ważność,
  • podmiot dla którego certyfikat został wystawiony,
  • klucz publiczny
  • podpis organu, wydającego certyfikat).

Jego autentyczność można sprawdzić wyłącznie znając klucz publiczny organu certyfikującego. Znajduje się on na certyfikacie, wystawionym przez organ certyfikujący wyższej instancji (CA). Zatem weryfikacja certyfikatu to prześledzenie łańcucha zaufania, zakończonego przez organ nadrzędny, cieszący się powszechnym zaufaniem, który jako jedyny wystawia certyfikat sam dla siebie.

CA

Certificate Authority ( Urząd Certyfikacji ) zajmuje się wydawaniem podpisanych certyfikatów, które potwierdzają tożsamość Alice i łączą tożsamość Alice z jej kluczem publicznym. Nadrzędny, najważniejszy CA podpisuje swój certyfikat używając swojego klucza prywatnego. Dalsze urzędy certyfikacji używają głównego CA do weryfikacji swojej tożsamości.

W naszym wypadku Alice i Bob muszę dogadać się, że będą używali CA to potwierdzenia swojej tożsamości. Alice prosi CA o wystawienie certyfikatu dla siebie przesyłając do CA swój klucz publiczny i potwierdzając swoją tożsamość. Następnie wysyła swój klucz publiczny oraz podpis do Bob-a. Bob sprawdza poprawność podpisu używając do tego certyfikatu wystawionego przez CA dla Alice. Jeśli podpis się zgadza (ma pewność, że klucz publiczny Alice należy do Alice), szyfruje swoją wiadomość przy użyciu tego klucza i wysyła ją do Alice.

Sporo teorii. Czas na kodowanie:

Aby testować i tworzyć kod wykorzystujący certyfikaty można utworzyć własny certyfikat wystarczy w linii poleceń C# wydać polecenie:

makecert testCert.cer

Następnie należy go zainstalować, podając w parametrach nazwę certyfikatu -n i miejsce jego przechowywania -ss.

makecert -n “CN=Przemek” -sr currentuser -ss testCertStore

W tym momencie posiadamy zainstalowany certyfikat, dzięki któremu Alice będzie mogła podpisać swoją wiadomość (swój klucz publiczny). Bob posiadając certyfikat Alice, będzie mógł sprawdzić czy wiadomość pochodzi od Alice.

  • Alice i Bob muszą najpierw pobrać certyfikat ze swojej maszyny.  Do tego celu mogą wykorzystać poniższy kod:
private static X509Certificate2 GetCertificate2()
        {
            var store = new X509Store("testCertStore", StoreLocation.CurrentUser);

            store.Open(OpenFlags.ReadOnly);

            foreach (var certificate in store.Certificates)
            {
                if (certificate.Subject == "CN=Przemek")
                {
                    return certificate;
                }
            }

            return null;
        }

Pobranie certyfikatu opiera na się na stworzeniu obiektu X509Store i podania nazwy miejsca, gdzie ten certyfikat jest przechowywany (testCertStore). Następnie pobieramy certyfikat zgodnie z jego nazwą. CN=Przemek jest to przyjęta nazwa certyfikatów X.509.

  • Następnie Alice musi podpisać swój klucz publiczny używając swojego certyfikatu:
 private static byte[] Sign(byte[] hash, X509Certificate2 cert)
        {
            var rsa = (RSACryptoServiceProvider)cert.PrivateKey;

            var result = rsa.SignHash(hash, CryptoConfig.MapNameToOID("SHA1"));

            return result;
        }

Aby to zrobić może użyć metody statycznej Sign, która pobiera dwa argumenty – hash (w naszym przypadku będzie to klucz publiczny Alice) jako tablicę bajtów oraz certyfikat Alice.

Podpisanie jest proste wystarczy wywołać dla obiektu RSACryptoServiceProvider metodę SignHash, której przesyłamy wiadomość Alice oraz metodę wyliczenia hash-a (SHA lub MD5). Do podpisu Alice używa prywatnego klucza swojego certyfikatu. Dzięki temu każdy z kim będzie się kontaktować będzie mógł użyć prywatnego klucza w certyfikacie do odszyfrowania podpisu Alice, jednocześnie tylko Alice będzie mogła podpisać swoje wiadomości.

W odpowiedzi dostajemy podpisany klucz publiczny Alice.

  • Teraz  Alice wysyła swój klucz publiczny wraz z podpisem do Bob-a. Bob musi teraz sprawdzić czy wiadomość jest podpisana przez Alice.
 public static bool CheckSign(byte[] hash, byte[] signedText, X509Certificate2 cert)
        {
            var rsaProvider = (RSACryptoServiceProvider) cert.PublicKey.Key;

            return rsaProvider.VerifyHash(hash, CryptoConfig.MapNameToOID("SHA1"), signedText);
        }

Metoda CheckSign ma zadanie sprawdzić czy hash, który do Nas dotarł jest taki sam jak ten w podpisane. Bob używa do tego celu również obiektu RSACryptoServiceProvider  jednak tym razem używa publicznego klucza certyfikatu. Metoda VerifyHash pozwala na porównanie zaszyfrowanej wiadomości z hash-em. Jeśli wynik będzie pozytywny Bob ma pewność, że wiadomość pochodzi o Alice.

Aby zobrazować działanie tych metod wystarczy wykonać poniższy kod:

public Bezpieczenstwo2()
        {
            var cert = GetCertificate2();

            var publicKey = "Przemek Walkowski";

            var publicKeyByte = new UnicodeEncoding().GetBytes(publicKey);

            var hash = new SHA1Managed().ComputeHash(publicKeyByte);

            var signedText = Sign(hash, cert);

            if (CheckSign(hash, signedText, cert))
            {
                Console.WriteLine("Tekst jest właściwie podpisany.");
            }
        }

Kod jest prosty. Pobieramy certyfikat, wyliczamy hash do klucza publicznego i podpisujemy wiadomość.

W tym momencie mamy rozwiązaliśmy wszystkie problemy z postu pierwszego czyli:

Pierwszy – jak Bob ma zaszyfrować wiadomość aby było to bezpieczne?” – może wymyśleć algorytm kryptograficzny lub skorzystać z gotowych.

Drugi to jeśli Bob ma już zaszyfrowaną wiadomość to jak Bob ma przekazać Alice klucz do deszyfracji wiadomości, tak aby Oscar nie mógł go podejrzeć?” – może użyć do tego algorytmu asymetrycznego i symetrycznego

Trzeci problem to skąd Bob wie, że wiadomość pochodzi od Alice a nie od Oscar-a?” – może zweryfikować tożsamość Alice używając certyfikatów.

W części III omówię metody CAS (Code access security) i SecureString.

 

Jeden komentarz do “Bezpieczeństwo w aplikacjach C#. Podstawy i nie tylko. Część II.

  1. Pingback: dotnetomaniak.pl

Możliwość komentowania została wyłączona.