Zagadnienia egzaminu 70-483 opisane w tej notatce:

  • Szyfrowanie symetryczne i asymetryczne - wybór odpowiedniego algorytmu szyfrowania.
  • Certyfikaty - tworzenie i zarządzanie certyfikatami, różne rodzaje certyfikatów i magazyny certyfikatów.
  • Klucze szyfrujące - tworzenie i zarządzanie kluczami szyfrującymi w .NET.
  • System.Security - implementacja przestrzeni nazw System.Security.
  • Hash data - tworzenie podpisów cyfrowych i opcje tworzenia skrótów danych.
  • Strumienie szyfrowane - użycie klasy CryptoStream do szyfrowania strumieni.
  • Zarządzanie assembly - wersjonowanie, podpisywani używając silnych nazw za pomocą Visual Studio oraz sn.exeGAC i użycie technik pozwalających na użycie assembly o różnych wersjach na tej samej maszynie.

Użycie szyfrowania

Szyfrowanie (ang. Encryption) jest procesem transformacji danych, który utrudnia osobie nieupoważnionej, aby odczytała te dane. Dane zaszyfrowane nazywane są szyfrogramem (ang. ciphertext). Odszyfrowywanie (ang. Decryption) jest procesem odwrotnym który przekształca zaszyfrowane dane w zwykłe dane. Im trudniej jest odszyfrować dane tym lepszy jest algorytm szyfrujący. Nie ma szyfrów niemożliwych do odszyfrowania ale niektóre algorytmy szyfrujące są tak skomplikowane, że czas ich odszyfrowania może zajmować bardzo długi czas.

Wybór odpowiedniego algorytmu szyfrującego

Szyfrowanie może być wykonywane na dwa sposoby. Pierwszy zwany jest szyfrowaniem symetrycznym (ang. symmetric encryption lub shared secret) a drugi zwany jest szyfrowaniem niesymetrycznym (ang. asymmetric encryption lub public key).

.NET Framework algorytmy szyfrowania zaimplementowane są na trzy sposoby:

  1. Klasy zarządzane - nazwy tych klas składają się z nazwy algorytmu i sufiksu Managed, np RijndaelManaged dla algorytmu Rijndael. Zarządzane implementacje są nieco wolniejsze od innych implementacji i nie są certyfikowane przez Federal Information Processing Standards (FIPS).
  2. Klasy opakowujące natywną implementację Cryptography API (CAPI) - nazwy tych klas składają się z nazwy algorytmu i sufiksu CryptoServiceProvider, np klasa DESCryptoServiceProvider dla algorytmu DES. Implementacje CAPI są odpowiednie dla starszych systemów.
  3. Klasy opakowujące natywną implementację Cryptography Next Generation API (CNG) - nazwy tych klas składają się z nazwy algorytmu i sufiksu CNG, np klasa ECDiffieHellmanCng dla algorytmu ECDH. Algorytmy CNG mogą być używane w systemach od Windows Vista.

Wszystkie klasy szyfrujące są zdefiniowane w przestrzeni nazw System.Security.Cryptography i są częścią rdzenia biblioteki .NET.

Szyfrowanie symetryczne

Szyfrowanie symetryczne wykonywane jest za pomocą klucza szyfrującego, który m jest tablica bajtowa. Ten sam klucz używany jest również do odszyfrowania danych. Algorytmy symetryczne polegają na tym, że tylko uprawniona osoba posiada dostęp do klucza szyfrującego. 
Algorytmy symetryczne w .NET używają trybu łańcuchowego nazywanego cipher block chaining. Taki rodzaj algorytmów działa w następujący sposób. Jeżeli ilość danych jest większa niż zdefiniowana wielkość bloku, to są dzielone na mniejsze bloki o zdefiniowanym rozmiarze. Pierwszy blok jest szyfrowany używając losowego bloku danych o tym samym rozmiarze, zwanym wektorem inicjującym (initialization vector - IV), oraz kluczem szyfrującym. Następne bloki są szyfrowane używając wyniku poprzedniego bloku i tego samego klucza. Rozmiar bloku zależy od algorytmu. Jeżeli ostatni blok jest mniejszy niż rozmiar, będzie dopełniony danymi do pełnego rozmiaru bloku. Aby odszyfrować dane trzeba posiadać IV oraz klucz szyfrujący. IV nie musi być trzymany w tajemnicy ale klucz szyfrujący owszem. 
Algorytmy symetryczne implementowane w .NET Framework:

  • AES - Advanced Encryption Standard. Ten algorytm jest implementowany przez dwie klasy: AesManaged i AesCryptoServiceProvider.
  • DES - Data Encryption Standard. Ten algorytm jest implementowany przez klasę DESCryptoServiceProvider.
  • RC2 - Rivest Cipher. Ten algorytm jest implementowany przez klasę RC2CryptoServiceProvider.
  • Rijndael - Ten algorytm jest implementowany przez klasę RijndaelManaged.
  • TripleDES - Triple Data Encryption Standard. Ten algorytm jest implementowany przez klasę TripleDESCryptoServiceProvider.

Wszystkie te klasy dziedziczą z klasy bazowej System.Security.Cryptography.SymmetricAlgorithm.

Lista właściwości klasy SymmetricAlgorithm:

  • BlockSize - pobiera lub ustawia rozmiar bloku.
  • FeedbackSize - pobiera lub ustawia rozmiar informacji zwrotnych. Rozmiar ten reprezentuje ilość danych w bitach, które są zawracane do następnej operacji szyfrowania lub odszyfrowywania. Rozmiar ten musi być mniejszy niż rozmiar bloku.
  • IV - pobiera lub ustawia IV. Kiedy tworzymy nową instancje algorytmu symetrycznego to IV generowane. Można również wygenerować je za pomocą metody GenerateIV. Rozmiar IV musi być taki sam jak rozmiar bloku podzielony przez osiem.
  • Key - pobiera lub ustawia klucz szyfrujący. Poprawne wielkości klucza szyfrującego zależne są od algorytmu i są wymienione w liście LegalKeySizes.
  • KeySize - pobiera lub ustawia rozmiar klucza szyfrującego.
  • LegelBlockSizes - Pobiera listę dozwolonych wartości rozmiaru bloku dla konkretnego algorytmu.
  • LegalKeySizes - Pobiera listę dozwolonych wartości rozmiaru klucza dla konkretnego algorytmu.
  • Mode - pobiera lub ustawia tryb operacji algorytmu. Wartości są enumeracją System.Security.Cryptography.CipherMode.
  • Padding - pobiera lub ustawia tryb wypełnienia używany przez algorytm. Wartości są enumeracją System.Security.Cryptography.PaddingMode.

Lista metod klasy SymmetricAlgorithm:

  • Clear - zwalnia wszystkie zasoby używane przez klasę algorytmu. Należy wywołać te metodę aby zwolnić wszystkie zasoby po zakończeniu pracy z obiektem szyfrującym.
  • Create() - metoda statyczna tworząca nowy obiekt szyfrujący z domyślnym algorytmem, dla .NET 4.5 jest to RijndaelManaged.
  • Create(String) - metoda statyczna tworząca nowy obiekt szyfrujący z wybranym algorytmem. Nazwa może być nazwą algorytmu lub nazwą klasy.
  • CreateDecryptor() - tworzy obiekt odszyfrowujący używając klucza i IV zdefiniowanych we właściwościach obiektu.
  • CreateDecryptor(Byte[], Byte[]) - tworzy obiekt odszyfrowujący używając klucza i IV zdefiniowanych jako parametry.
  • CreateEncryptor() - tworzy obiekt szyfrujący używając klucza i IV zdefiniowanych we właściwościach obiektu.
  • CreateEncryptor(Byte[], Byte[]) - tworzy obiekt odszyfrowujący używając klucza i IV zdefiniowanych jako parametry.
  • GenerateIV - tworzy losowy wektor IV używany przez algorytm. Zwykle nie wywołuje się tej metody bezpośrednio.
  • GenerateKey - generuje losowy klucz używany przez algorytm.
  • ValidKeySize - zwraca true jeżeli rozmiar klucza jest odpowiedni dla danego algorytmu.

Przebieg szyfrowania zwykłego tekstu algorytmem symetrycznym jest następujący:

  1. Tworzymy obiekt algorytmu symetrycznego poprzez wywołanie metody Create klasy SymmetricAlgorithm, opcjonalnie ustawiając parametr z nazwą wybranego algorytmu.
  2. Jeżeli chcemy to możemy ustawić klucz szyfrujący i wektor IV, lecz nie jest to wymagane gdyż są one domyślnie generowane.
  3. Generujemy obiekt szyfrujący wywołując metodę CreateEncryptor. Można wybrać klucz i wektor lecz nie jest to konieczne.
  4. Wywołujemy metodę TransformFinalBlock na obiekcie szyfrującym, która pobiera na wejściu tablicę bajtową reprezentującą dane, offset - gdzie zacząć szyfrowanie i długość danych do szyfrowania. Metoda zwraca zaszyfrowane dane.

Przykład szyfrowania:

byte[] EncryptData(byte[] plainData, byte[] IV, byte[] key) {

SymmetricAlgorithm cryptoAlgorythm = SymmetricAlgorithm.Create();

ICryptoTransform encryptor = cryptoAlgorythm.CreateEncryptor(key, IV);

byte[] cipherData = encryptor.TransformFinalBlock(plainData, 0,
plainData.Length);

return cipherData;
}

Przebieg odszyfrowania zaszyfrowanego tekstu algorytmem symetrycznym jest następujący:

  1. Tworzymy obiekt algorytmu symetrycznego poprzez wywołanie metody Create klasy SymmetricAlgorithm, opcjonalnie ustawiając parametr z nazwą wybranego algorytmu który został użyty do szyfrowania.
  2. Jeżeli chcemy to możemy ustawić klucz szyfrujący i wektor IV, lecz nie jest to wymagane ponieważ można podać je później.
  3. Tworzymy obiekt odszyfrowujący wywołując metodę CreateDecryptor. Jeżeli nie podaliśmy klucza i wektora wcześniej to teraz musimy je ustawić. Klucz szyfrujący i wektor IV muszą być takie same jak przy szyfrowaniu.
  4. Wywołujemy metodę TransformFinalBlock na obiekcie odszyfrowującym, który pobiera na wejściu tablicę bajtową z zaszyfrowanymi danymi, offset - gdzie zacząć odszyfrowywanie i długość danych do odszyfrowywania. Metoda zwraca odszyfrowane dane.

Przykład odszyfrowania:

byte[] DecryptData(byte[] cipherData, byte[] IV, byte[] key) {

SymmetricAlgorithm cryptoAlgorythm = SymmetricAlgorithm.Create();

ICryptoTransform decryptor = cryptoAlgorythm.CreateDecryptor(key, IV);

byte[] plainData = decryptor.TransformFinalBlock(cipherData, 0,
cipherData.Length);

return plainData;
}

Największym wyzwaniem przy użyciu algorytmów symetrycznych jest zachowanie tajności klucza szyfrującego.

Szyfrowanie asymetryczne

Głównym powodem używania szyfrowania asymetrycznego jest uniknięcie udostępniania klucza szyfrowania. Szyfrowanie asymetryczne używa dwóch matematycznie powiązanych kluczy, które uzupełniają się nawzajem. Wiadomość zaszyfrowana jednym z nich może być odszyfrowana jedynie drugim. Jeden klucz jest kluczem publicznym którym dane są szyfrowane. Drugi klucz który służy do odszyfrowania wiadomości nazywany jest kluczem prywatnym.

Szyfrowanie asymetryczne jest wolniejsze od symetrycznego, lecz jego największą zaletą jest to, że nie potrzeba przekazywać klucza szyfrującego aby algorytm działał.

Algorytmy asymetryczne implementowane w .NET Framework:

  • DSA - Digital Signature Algorithm. Służy do tworzenia podpisów cyfrowych, które pomagają chronić integralność danych. Klasa implementująca ten algorytm to DSACryptoServiceProvider. Algorytmu DSA należy używać jedynie dla zgodności z przestarzałymi aplikacjami.
  • ECDiffieHellman - Elliptic Curve Diffie-Hellman Algorithm. Klasa implementująca ten algorytm to ECDiffieHellmanCng.
  • ECDsa - Elliptic Curve Digital Signature Algorithm (ECDSA). Klasa implementująca ten algorytm to ECDsaCng.
  • RSA - Klasa implementująca ten algorytm to RSACryptoServiceProvider.

Wszystkie te klasy dziedziczą z klasy bazowej System.Security.Cryptography.AsymmetricAlgorithm.

Przebieg szyfrowania zwykłego tekstu algorytmem asymetrycznym jest następujący:

  1. Uzyskania klucza publicznego odbiorcy.
  2. Utworzenie nowego obiektu asymetrycznego szyfrowania.
  3. Ustawienie klucza publicznego.
  4. Szyfrowanie danych
  5. Wysłanie danych do odbiorcy.

    Przebieg odszyfrowania zaszyfrowanej wiadomości algorytmem asymetrycznym jest następujący:

    1. Odebranie danych od wysyłającego.
    2. Utworzenie nowego obiektu asymetrycznego szyfrowania.
    3. Ustawienie klucza prywatnego.
    4. Odszyfrowanie danych.

Jeżeli dane zostały zmienione lub nie zostały zaszyfrowane odpowiadającym kluczem publicznym to zgłoszony zostanie wyjątek CryptographicException.

Istnieją dwa sposoby obsługi kluczy. Pierwszy polega na bezpośrednim wysłaniu klucza publicznego. Drugi polega na umieszczeniu klucza w pojemniku dostawcy usług kryptograficznych.

Jednym z typowych scenariuszy jest użycie szyfrowania asymetrycznego do wymiany kluczy potrzebnych do szyfrowania symetrycznego stosując następujący przepływ:

  1. Obie strony generują pary kluczy prywatnych i publicznych.
  2. Strony wymieniają się kluczami publicznymi.
  3. Każda ze stron generuje klucz symetryczny który może służyć do szyfrowania danych.
  4. Każda ze stron szyfruje klucz symetryczny używając algorytmu asymetrycznego i klucza publicznego otrzymanego od drugiej strony.
  5. Każda ze stron wysyła zaszyfrowany klucz symetryczny do drugiej strony.
  6. Obie strony używają swojego klucza prywatnego do odszyfrowania klucza szyfrowania symetrycznego.
  7. Strony zaczynają wymieniać się danymi używając algorytmu symetrycznego i klucza z poprzedniego kroku.

Innym typowym scenariuszem jest użycie szyfrowania asymetrycznego do podpisania danych, zapewniając w ten sposób zarówno autentyczność i tożsamość.

Szyfrowanie strumieni

Strumienie reprezentują plik, urządzenie I/O lub kanał komunikacyjny i potrafią wykonywać trzy podstawowe zadania:

  • Read - można czytać dane ze strumienia do struktury danych.
  • Write - można zapisać dane ze struktury danych do strumienia.
  • Seek - można zmienić aktualną pozycję w strumieniu gdzie następna operacja odczytu lub zapisu działa.

Bardzo ważną właściwością strumieni w .NET jest to, że mogą być łączone poprzez przekazywanie danych wyjściowych ze strumienia do wejścia innego strumienia. Czasami chcemy aby dane przechodzące przez strumień były zaszyfrowane. Można zaszyfrować dane przed wysłaniem do strumienia bądź połączyć strumienie tak aby jeden z nich odpowiadał za szyfrowanie. Jednym ze strumieni w .NET jest CryptoStream, który potrafi szyfrować i deszyfrować dane które przez niego przechodzą.

Praca ze strumieniami szyfrowanymi wygląda następująco:

  1. Tworzymy obiekt algorytmu symetrycznego poprzez wywołanie metody Create klasy SymmetricAlgorithm, opcjonalnie wybierając nazwę algorytmu szyfrującego jako parametr wejściowy.
  2. Jeżeli chcemy to możemy ustawić klucz szyfrujący i wektor IV, lecz nie jest to wymagane gdyż są one domyślnie generowane.
  3. Tworzymy obiekt szyfrujący wywołując metodę CreateEncryptor. Można wybrać klucz i wektor lecz nie jest to konieczne.
  4. Tworzymy obiekt strumienia CryptoStream. Konstruktor przyjmuje trzy parametry. Pierwszym jest strumień szyfrowanych danych; drugi to obiekt szyfrujący; trzeci to tryb strumienia który w tym wypadku to Write.
  5. Zapisujemy dane do obiektu strumienia CryptoStream wywołując jedną z metod zapisu udostępnianych przez CryptoStream, używając StreamWriter, lub łącząc go z innym strumieniem.
  6. Kiedy skończymy zapis należy wyczyścić obiekt CryptoStream wywołując metodę Clear po czym Dispose.

Przykładowy blok kodu demonstrujący taką operację:

byte[] EncryptString(string plainData, byte[] IV, byte[] key) {

SymmetricAlgorithm cryptoAlgorythm = SymmetricAlgorithm.Create();

ICryptoTransform encryptor = cryptoAlgorythm.CreateEncryptor(key, IV);

byte[] cipherData = newbyte[0];

using (MemoryStream msEncrypt = new MemoryStream()) {
using (CryptoStream csEncrypt = new CryptoStream(msEncrypt,
encryptor,
CryptoStreamMode.Write)) {

StreamWriter swEncrypt = new StreamWriter(csEncrypt);

swEncrypt.Write(plainData);
swEncrypt.Close();
csEncrypt.Clear();
cipherData = msEncrypt.ToArray();
}
}

return cipherData;
}

Odszyfrowanie strumienia wygląda następująco:

  1. Tworzymy obiekt algorytmu symetrycznego poprzez wywołanie metody Create klasy SymmetricAlgorithm, opcjonalnie wybierając nazwę algorytmu szyfrującego jako parametr wejściowy.
  2. Jeżeli chcemy to możemy ustawić klucz szyfrujący i wektor IV, możemy również uczynić to w następnym kroku.
  3. Tworzymy obiekt deszyfrujący wywołując metodę CreateDecryptor. Jeżeli nie uczyniliśmy tego wcześniej to teraz trzeba wybrać klucz i wektor które zostały użyte do szyfrowania.
  4. Tworzymy obiekt strumienia CryptoStream. Konstruktor przyjmuje trzy parametry. Pierwszym jest strumień szyfrowanych danych; drugi to obiekt deszyfrujący; trzeci to tryb strumienia który w tym wypadku to Read.
  5. Odczytujemy dane z obiektu strumienia CryptoStream za pomocą jednej z jego metod odczytu, używając obiektu StreamReader lub połączenia do innego strumienia.
  6. Kiedy skończymy zapis należy wyczyścić obiekt CryptoStream wywołując metodę Clear po czym Dispose.

Przykładowy blok kodu demonstrujący taką operację:

stringDecryptString(byte[] cipherData, byte[] IV, byte[] key) {

SymmetricAlgorithm cryptoAlgorythm = SymmetricAlgorithm.Create();

ICryptoTransform decryptor = cryptoAlgorythm.CreateDecryptor(key, IV);

string plainText = string.Empty;

using (MemoryStream msDecrypt = new MemoryStream(cipherData)) {
using (CryptoStream csDecrypt = new CryptoStream(msDecrypt,
decryptor,
CryptoStreamMode.Read)) {

StreamReader srDecrypt = new StreamReader(csDecrypt);

plainText = srDecrypt.ReadToEnd();
srDecrypt.Close();
csDecrypt.Clear();
}
}

return plainText;
}

Bardzo często spotykanym scenariuszem jest zaszyfrowanie i skompresowanie danych po czym wysłanie ich przez sieć. Kompresowanie tekstu jest bardziej efektywne niż danych binarnych więc należy zacząć od operacji kompresji danych po czym dopiero je zaszyfrować. Strona odbierająca musi wykonać operacje w odwrotnej kolejności.

Funkcja skrótu danych (Hash)

Funkcja skrótu to proces mapowania danych binarnych o zmiennej długości do danych binarnych stałego rozmiaru. Stosowanie tej samej funkcji skrótu do dwóch identycznych struktur danych, daje taki sam efekt. 
Funkcje skrótu są używane w kilku standardowych scenariuszach:

  • Indeksowanie danych - zamiast dopasować dane, gdy klucz indeksu posiada zmienną długość, można obliczyć 
    wartość skrótu klucza indeksu i wyszukiwać tą wartość zamiast klucza. Wartość funkcji skrótu jest zwykle krótsza niż oryginalna wartość, więc jej wyszukiwanie jest zwykle szybsze. Istnieje możliwość, że dwa lub więcej kluczy indeksu może wywoływać tę samą wartość skrótu. W takim wypadku algorytm indeksujący używa techniki zwanej hash bucket gdzie wartości indeksu posiadające tą samą wartość skrótu są grupowane razem.
  • Integralność danych - integralności danych stosuje się w celu zapewnienia, że dane dotrą do miejsca przeznaczenia bez zmian. Nadawca oblicza kryptograficzny skrót danych, które chce wysłać, a następnie wysyła te dane, skrót i informacje o technice stosowanej do obliczenia skrótu do odbiorcy. Odbiorca może zastosować ten sam algorytm na danych i porównać wynik z otrzymanymi danymi. Jeżeli są takie same to znaczy, że dane nie uległy zmianie po wykonaniu skrótu.
  • Autentyczność danych - autentyczności danych stosuje się, gdy odbiorca chce upewnić się, że dane pochodzą od właściwego nadawcy i, że nie zmieniły się po drodze. Nadawca oblicza kryptograficzny skrót danych i podpisuje go własnym kluczem prywatnym. Odbiorca tworzy ponownie skrót i odszyfrowuje otrzymany podpis używając klucza publicznego wysyłającego po czym porównuje dane.
  • Przechowywanie haseł - przechowywanie haseł jako zwykły tekst jest niebezpieczną i niezalecaną techniką. Aby chronić hasła zwykle stosuje się do nich funkcje skrótu przed ich zapisaniem.

Istnieją dwa rodzaje algorytmów funkcji skrótów: z kluczem lub bez klucza. Algorytmy bez klucza są zwykle używane tylko w scenariuszach integralności danych.

Lista Algorytmów tworzenia skrótów zaimplementowanych w .NET:

  • SHA1 - implementacja algorytmu SHA tworząca skrót o rozmiarze 160 bitów.
  • SHA256 - implementacja algorytmu SHA tworząca skrót o rozmiarze 256 bitów.
  • SHA512 - implementacja algorytmu SHA tworząca skrót o rozmiarze 512 bitów.
  • SHA384 - implementacja algorytmu SHA tworząca skrót o rozmiarze 384 bitów.
  • MD5 - implementacja algorytmu Md5. Używaj tylko dla kompatybilności ze starymi aplikacjami.
  • RIPEMD160 - implementacja algorytmu RIPEMD. Używaj tylko dla kompatybilności ze starymi aplikacjami.

Wszystkie klasy skrótów dziedziczą z abstrakcyjnej klasy bazowej System.Security.Cryptography.HashAlgorithm.

Właściwości klasy HashAlgorithm:

  • CanReuseTransform - właściwość tylko do odczytu zwracająca true jeżeli aktualna transformacja może być użyta ponownie.
  • CanTransformMultipleBlocks - właściwość tylko do odczytu zwracająca true jeżeli wiele bloków może być transformowanych.
  • Hash - właściwość tylko do odczytu zwracająca obliczoną wartość skrótu.
  • HashSize - właściwość tylko do odczytu zwracająca rozmiar obliczonego skrótu w bitach.
  • InputBlockSize - właściwość tylko do odczytu zwracająca rozmiar bloku wejściowego.
  • OutputBlockSize - pobiera rozmiar bloku wyjściowego.

Metody klasy HashAlgorithm:

  • Clear - zwalnia wszystkie używane zasoby.
  • ComputeHash(Byte[]) - wylicza skrót dla tablicy bajtów.
  • ComputeHash(Stream) - wylicza skrót dla strumienia.
  • ComputeHash(Byte[], Int32, Int32) - wylicza skrót dla regionu w tablicy bajtów.
  • Create() - tworzy nowy obiekt algorytmu skrótów używając domyślnego algorytmu, w .NET 4.5 jest to SHA1.
  • Create(String) - tworzy nowy obiekt algorytmu skrótów używając wybranego algorytmu.
  • TransformBlock - oblicza skrót dla określonego regionu wejściowej tablicy bajtowej inputBuffer i kopiuje wynik do określonego regionu w tablicy outputBuffer.
  • TransformFinalBlock - oblicza skrót dla określonego regionu wejściowej tablicy bajtowej inputBuffer.

Zwykle używamy tylko metod Create i ComputeHash. Metody TransformBlock i TransformFinalBlock są używane gdy chcemy obliczyć skrót tylko dla części danych.

Algorytmy skrótów z kluczem dziedziczą z klasy System.Security.Cryptography.KeyedHashAlgorithm
W porównaniu z algorytmami bez klucza klasaKeyedHashAlgorithm posiada jedną istotną właściwość:

  • Key - pobiera lub ustawia wartość klucza używanego przez algorytm. jeżeli chcemy zmienić klucz po rozpoczęciu operacji tworzenia skrótu to zgłoszony zostanie wyjątek CryptographicException.

Schemat działania przy tworzeniu skrótu jest następujący:

  1. Tworzymy obiekt algorytmu funkcji skrótu.
  2. Jeżeli algorytm używa klucza to ustawiamy wartość klucza.
  3. Wywołujemy metodę ComputeHash.
  4. Zapisujemy skrót danych.

Przykładowy blok kodu demonstrujący taką operację:

stringComputeHash(string input)
{
HashAlgorithm sha = SHA256.Create();
byte[] hashData = sha.ComputeHash(Encoding.Default.GetBytes(input));
return Convert.ToBase64String(hashData);
}

Schemat działania przy weryfikacji skrótu dla danych jest następujący:

  1. Tworzymy obiekt algorytmu funkcji skrótu taki sam jak przy tworzeniu skrótu.
  2. Jeżeli algorytm używa klucza to ustawiamy wartość klucza taką samą jak przy tworzeniu skrótu.
  3. Wyodrębniamy oryginalny skrót danych.
  4. Wywołujemy metodę ComputeHash.
  5. Porównujemy oryginalny skrót z tym wyliczonym.

Przykładowy blok kodu demonstrujący taką operację:

boolVerifyHash(string input, string hashValue) 
{
HashAlgorithm sha = SHA256.Create();
byte[] hashData = sha.ComputeHash(Encoding.Default.GetBytes(input));
return Convert.ToBase64String(hashData) == hashValue;
}

Tworzenie i zarządzanie certyfikatami

Kiedy dwie strony komunikują się ze sobą to muszą mieć pewność, że porozumiewają się z właściwym partnerem. Przykładowo kiedy wykonujemy transakcje bankową w internecie, chcemy mieć pewność, że jesteśmy na stronie naszego banku a nie na innej stronie która podszywa się pod stronę naszego banku. Chcemy wiedzieć również, że komunikacja jest bezpieczna. Dla aplikacji sieciowych istnieją dwa protokoły które rozwiązują ten problem: Transport Layer Security (TLS) i Secure Socket Layer (SSL). Oba te protokoły szyfrują dane i zapewniają autentyczność danych. Dla autentyczności używają Public Key Infrastructure (PKI), która jest niezbędna do obsługi certyfikatów cyfrowych. PKI używa notacji nazywanej Certificate Authority (CA)CA jest to jednostka która wystawia certyfikaty cyfrowe. Certyfikaty cyfrowe wiąże klucz publiczny z tożsamością. Dzięki temu, kiedy dwie strony chcą się komunikować i nadawca chce się upewnić, że komunikuje się z odpowiednim odbiorcą, nadawca może użyć PKI do zweryfikowania tożsamości odbiorcy. 
Zasada działania jest prosta. Można albo wybrać bezpośrednio, że ufamy drugiej stronie albo zaufać trzeciej stronie która weryfikuje tożsamość innych podmiotów. Druga opcja jest hierarchiczna, co oznacza, że liczba jednostek którym zdecydujesz się zaufać jest ograniczona. Lecz te jednostki mogą weryfikować tożsamość innych podmiotów co czyni je również podmiotami zaufanymi itd.
Certyfikaty najwyższego poziomu, którym zdecydujesz się zaufać nazywane są certyfikatami głównymi (ang. root certificates). Normalnie nie dodajemy żadnego certyfikatu głównego. Przychodzą one wraz z instalacją systemu operacyjnego lub jego aktualizacjami. Certyfikaty najwyższego poziomu którym ufa nasz komputer można podejrzeć w panelu sterowania w opcjach internetowych. W zakładce Content należy kliknąć guzik Certyficates i wybrać zakładkę Trusted Root Certification Authorities. Okno powinno być podobne do poniższego:
1457878534340
jeżeli strona chce aby jej tożsamość była gwarantowana, musi jedynie zweryfikować jej tożsamość w jednym z podmiotów które darzysz zaufaniem. Jednym z przykładów jest Microsoft. Na komputerze istnieje Root Certificate Authority zwane GTE CyberTrust Global Root. jeżeli wprowadzimy do przeglądarki adres https://www.microsoft.com i podejrzymy certyfikat strony to zobaczymy dialog podobny do poniższego:
1457878482778

Standard który jest używany przez PKI to X.509, który określa format PKIsCertificate Revocation List (CLR), atrybuty certyfikatów i sposób walidacji ścieżek certyfikatów. 
.NET Framework implementuje standard X.509. Wszystkie klasy które są potrzebne do zarządzania certyfikatami znajdują się w przestrzeni nazw System.Security.Cryptography.X509Certificates. Jeżeli posiadamy klucz prywatny który jest zainstalowany lokalnie to możemy odszyfrować dane zapisane odpowiadającym mu kluczem publicznym.

Microsoft oferuje używanie narzędzia Makecert.exe (Certificate Creation Tool), które może być używane do tworzenia certyfikatów.

Do pracy programistycznej z certyfikatami można użyć klasy X509Certificate2
Certyfikaty używają pojęcia magazynów certyfikatów, które są miejscami, w których certyfikaty są bezpiecznie przechowywane. W .NET magazyny są zaimplementowane w klasie X509Store. System Windows oferuje dwie lokacje dla magazynów, które są reprezentowane przez enumerację StoreLocation
Wartości enumeracji StoreLocation:

  • CurrentUser - magazyn certyfikatów aktualnego użytkownika.
  • LocalMachine - magazyn certyfikatów wspólny dla wszystkich użytkowników na maszynie lokalnej.

System Windows oferuje osiem predefiniowanych magazynów które są reprezentowane przez enumerację StoreName. Możliwe wartości enumeracji StoreName:

  • AddressBook - magazyn certyfikatów dla innych użytkowników.
  • AuthRoot - magazyn certyfikatów dla niezależne urzędów certyfikacji CA.
  • CertificateAuthority - magazyn certyfikatów dla pośrednich CA.
  • Disallowed - magazyn unieważnionych certyfikatów.
  • My - magazyn certyfikatów personalnych.
  • Root - magazyn certyfikatów dla zaufanych głównych urzędów certyfikacji.
  • TrustedPeople - magazyn certyfikatów dla bezpośrednio zaufanych ludzi i zasobów.
  • TrustedPublisher - magazyn certyfikatów dla bezpośredniego zaufanych wydawców.

Klasa X509Store posiada właściwość Name, której można użyć jeżeli chcemy stworzyć swój własny magazyn.

Poniższy fragment kodu demonstruje jak użyć klasy X509Store aby pokazać informacje o certyfikatach głównych X509Certificates zainstalowanych na maszynie lokalnej:

classCertificateTest {
staticvoidMain() {
X509Store store = new X509Store(StoreName.Root,
StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);

Console.WriteLine("Friendly Name\t\t\t\t\t Expiration date");
foreach (X509Certificate2 certificate in store.Certificates) {
Console.WriteLine("{0}\t{1}", certificate.FriendlyName
, certificate.NotAfter);
}
store.Close();
}
}

Zarządzanie kluczami

Czasami chcemy aby nasze dane były po prostu zabezpieczone bez zastanawiania się nad rodzajem algorytmu lub sposobie implementacji. Przykładowo aby algorytm szyfrowania był efektywny trzeba chronić wspólne hasło dla algorytmu symetrycznego i klucz prywatny dla algorytmu asynchronicznego. 
Aby rozwiązać tego typu problemy .NET Framework oferuje przestrzeń nazw System.Security.Cryptography i klasę ProtectedData. Ta klasa posiada dwie metody statyczne: Protect i Unprotect. Ich sygnatury są następujące:

publicstaticbyte[] Protect(
byte[] userData,
byte[] optionalEntropy,
DataProtectionScope scope
)

publicstaticbyte[] Unprotect(
byte[] encryptedData,
byte[] optionalEntropy,
DataProtectionScope scope
)

Pierwszym parametrem są dane do zaszyfrowania lub odszyfrowania. Drugim parametrem jest optionalEntropy używany do zwiększenia złożoności zaszyfrowanych danych. trzecim jest DataProtectionScope, określa on, kto może odszyfrować dane. Jego wartości toDataProtectionScope.CurrentUser lub DataProtectionScope.LocalMachine.

Poniższy fragment kodu demonstruje użycie klasy ProtectedData do zaszyfrowania ciągu znaków, które odszyfrować może tylko aktualny użytkownik.

byte[] ProtectString(string data)
{
byte[] userData = System.Text.Encoding.Default.GetBytes(data);
byte[] encryptedData = ProtectedData.Protect(userData, null,
DataProtectionScope.CurrentUser);
return encryptedData;
}

Jaki algorytm wybrać

Firma Microsoft rekomenduje następujące algorytmy do użycia w następujących sytuacjach:

  • Dla prywatności danych algorytm Aes.
  • Dla integralności danych algorytm HMACSHA256 lub HMACSHA512.
  • Do cyfrowego podpisu algorytmy RSA lub ECDsa.
  • Do wymiany kluczy algorytmy RSA lub ECDiffieHellman.
  • Do generowania losowych liczb klasa RNGCryptoServiceProvider.
  • Do generowania klucza z hasła Rfc2898DeriveBytes.

Poniższy fragment kodu zawiera pełny przykład użycia algorytmu asymetrycznego RSA:

using System;
using System.Security.Cryptography;
using System.Text;

namespaceEncryption.Asymmetric
{
publicclassRSASample
{
publicstaticvoidRun() {

string keyContainerName = "MyKeyContainer";
string clearText = "This is the data we want to encrypt!";
var cspParams = new CspParameters();
cspParams.KeyContainerName = keyContainerName;

RSAParameters publicKey;
RSAParameters privateKey;

using (var rsa = new RSACryptoServiceProvider(cspParams)) {

rsa.PersistKeyInCsp = true;
publicKey = rsa.ExportParameters(false);
privateKey = rsa.ExportParameters(true);

rsa.Clear();
}

byte[] encrypted = EncryptUsingRSAParam(clearText, publicKey);
string decrypted = DecryptUsingRSAParam(encrypted, privateKey);

Console.WriteLine("Asymmetric RSA");
Console.WriteLine("Asymmetric RSA - Using RSA Params");
Console.WriteLine("Encrypted:{0}", Convert.ToBase64String(encrypted));
Console.WriteLine("Decrypted:{0}", decrypted);
Console.WriteLine();

Console.WriteLine("Asymmetric RSA - Using Persistent Key Container");
encrypted = EncryptUsingContainer(clearText, keyContainerName);
decrypted = DecryptUsingContainer(encrypted, keyContainerName);
Console.WriteLine("Encrypted:{0}", Convert.ToBase64String(encrypted));
Console.WriteLine("Decrypted:{0}", decrypted);
Console.WriteLine();
}

staticbyte[] EncryptUsingRSAParam(stringvalue, RSAParameters rsaKeyInfo)
{
using (var rsa = new RSACryptoServiceProvider())
{
rsa.ImportParameters(rsaKeyInfo);
byte[] encodedData = Encoding.Default.GetBytes(value);
byte[] encryptedData = rsa.Encrypt(encodedData, true);

rsa.Clear();
return encryptedData;
}
}

staticstringDecryptUsingRSAParam(byte[] encryptedData, RSAParameters rsaKeyInfo) {
using (var rsa = new RSACryptoServiceProvider())
{
rsa.ImportParameters(rsaKeyInfo);
byte[] decryptedData = rsa.Decrypt(encryptedData, true);
string decryptedValue = Encoding.Default.GetString(decryptedData);

rsa.Clear();
return decryptedValue;
}
}

staticbyte[] EncryptUsingContainer(stringvalue, string containerName)
{
var cspParams = new CspParameters();
cspParams.KeyContainerName = containerName;
using (var rsa = new RSACryptoServiceProvider(cspParams))
{
byte[] encodedData = System.Text.Encoding.Default.GetBytes(value);
byte[] encryptedData = rsa.Encrypt(encodedData, true);

rsa.Clear();
return encryptedData;
}
}

staticstringDecryptUsingContainer(byte[] encryptedData, string containerName)
{
var cspParams = new CspParameters();
cspParams.KeyContainerName = containerName;
using (var rsa = new RSACryptoServiceProvider(cspParams))
{
byte[] decryptedData = rsa.Decrypt(encryptedData, true);
string decryptedValue = Encoding.Default.GetString(decryptedData);

rsa.Clear();
return decryptedValue;
}
}
}
}

Zarządzanie assembly

Wynikiem kompilacji projektu są pliki wykonywalne, nazywane assembly. Assembly są blokami budującymi każdą aplikację .NET. Są one podstawową jednostką do wdrożenia, wersjonowania, ponownego użycia i bezpieczeństwa.

Assembly zawiera Intermediate Language (IL), metadane i zasoby. IL jest rezultatem kompilacji kodu źródłowego, który może być zrozumiany przez Common Language Runtime (CLR)CLR posiada komponent kompilatora Just In Time (JIT), który kompiluje kod IL do kodu binarnego który może być uruchomiony na wybranej platformie, np Windows. Assembly może składać się z jednego lub wielu plików. Visual Studio generuje tylko assembly jedno plikowe.

Assembly jest otrzymywane przez kompilację projektu. Wynikiem assembly może być plik procesowy EXE lub plik biblioteki DLL. Różnicą pomiędzy tymi dwoma jest obecność głównego punktu wejścia w plikach procesowych czyli metody która jest uruchamiana kiedy aplikacja startuje np metoda Main.

Wersja Assembly

Oprogramowanie ewoluuje i sposobem na oznaczenie tego jest tworzenie nowych wersji, gdzie każda nowa wersja dodaje nowe funkcje, naprawia błędy lub jest kompletnie przepisana. 
Wersja assembly w .NET jest zaimplementowana jako ciąg znaków, zwykle składająca się z czterech części numerycznych odseparowanych kropkami: MajorMinorBuild i Revision. Przykładowo wszystkie assembly w .NET Framework 4.5 posiadają wersję 4.0.30319.17929. 
Oznaczenia części z których składa się wersja:

  • Revision - jest zwykle losowym numerem rozróżniającym wersje o tym samym numerze Build. Zwykle zmienia się ją przy każdym budowaniu projektu.
  • Build - jest to zwykle numer który wzrasta każdego dnia, kiedy pracujemy nad aktualną wersją produktu. Zwykle numer zmieniany jest każdej nocy przez nocny proces budowania.
  • Minor - numer ten jest zwiększany z każdym publicznym wydaniem produktu. Zwykle dwie wersje tego samego produktu które są kompatybilne wstecz mają ten sam numer Major lecz różnią się właśnie numerem Minor.
  • Major - numer zwiększany za każdym razem, gdy masz główne wydanie swojego produktu, albo przez zmianę istniejących funkcji lub przepisywanie całej aplikacji.
Wersję aplikacji można zmieniać na dwa sposoby. Pierwszym jest zmiana wersji w oknie właściwości projektu. W oknie właściwości Assembly naciskamy guzik Assembly Information i pojawia się okno dialogowe w którym mozna ustawić numer wersji podobne do poniższego:
1458070981360
Drugim sposobem jest edycja wersji w pliku AssemblyInfo.cs który znajduje się w folderze Properties każdego projektu. Na końcu pliku można znaleźć linię z wersją projektu przypominającą poniższą linię:
[assembly: AssemblyVersion("2.1.42.15")]

Właściwość Assembly Version jest ignorowana przez CLR, chyba że jest ona używana wraz z silnymi nazwami.

Podpisywanie assembly używając silnych nazw

Problem z plikami DLL pojawia się kiedy wiele aplikacji używa tej samej biblioteki. Jeżeli aplikacje są skompilowane z różnymi wersjami tej samej biblioteki to ta wersja która została zainstalowana najpóźniej nadpisuje pozostałe. Jeżeli występują problemy z kompatybilnością pomiędzy wersjami biblioteki to niektóre aplikacje mogą działać niepoprawnie a nawet nie działać wcale. Biblioteka .NET rozwiązała ten problem wprowadzając nowy koncept nazywany side-by-side versioning. Polega to rozróżnieniu pomiędzy różnymi wersjami tej samej biblioteki, przy jednoczesnym zapewnieniu, że dwie różne wersje pochodzą z tego samego źródła. 
.NET nazwa assembly składa się z czterech części:

  • Przyjaznej nazwy.
  • Wersji.
  • Kultury.
  • PublicKeyToken
Przyjazna nazwa jest nazwą assembly, zwykle bez rozszerzenia. 
Wersja to wersja assembly omówiona w poprzedniej sekcji. 
Kultura jest używany, gdy chcemy zlokalizować swoją aplikację na różnych rynkach. Aby zlokalizować aplikację, należy utworzyć jedną assembly zawierającą kod i jedną assembly dla każdego regionu aplikacji. Assembly dla regionów zawierają jedynie zasoby jak np przetłumaczone teksty lub zdjęcia. Assembly wykonywalna posiada zawsze kulturę neutralną.
Aby assembly posiadała silną nazwę należy ją podpisać parą kluczy prywatnego i publicznego. Klucz prywatny jest używany do cyfrowego podpisania assembly a klucz publiczny jest używany do weryfikacji tego podpisu. Public Key Token jest 64 bitowym skrótem klucza publicznego assembly. Skrót jest używany aby zaoszczędzić miejsce, ponieważ klucz jest dużo dłuższy.
Parę kluczy prywatnego i publicznego dla assembly można wygenerować na dwa sposoby. Pierwszym jest użycie Visual Studio. W oknie właściwości wybieramy zakładkę Signing i zaznaczamy pole wyboru Sign the Assembly jak na obrazku poniżej:
1458145397514
Wybieramy <New...> i pokaże się okno dialogowe jak poniższe:
str_name

W oknie dialogowym można ustawić nazwę pliku klucza, hasło i algorytm używany do podpisu cyfrowego.

Drugim sposobem generowania kluczy jest użycie programu sn.exe w oknie Developer Command Prompt i użycie komendy:

sn.exe-kmyFile.snk

Utworzy to nowy klucz który należy wybrać opcją <Browse…> w oknie wyboru klucza.

Kiedy assembly będzie wymagała podpisu cyfrowego to kompilator podpiszę assembly używając klucza prywatnego i dołączy klucz publiczny do assembly dla możliwości weryfikacji. następnym krokiem jest utworzenie skrótu dla klucza publicznego aby utworzyć Public Key Token i również dołączyć go do assembly. 
Pełna nazwa assembly jest nazywana Fully Qualified Name (FQN). Przykładowo FQN dla assembly System w .NET 4.5 to:

System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089

Side-by-Side Versioning

Silne nazwy miałyby tylko niewielką wartość gdyby nie potęga uruchamiania różnych wersji tego samego assembly obok siebie. 
Wersionowanie obok siebie działa tylko z silnie nazwanymi assembly ponieważ potrzebuje aby były one wdrożone do Global Assembly Cache (GAC)
Kiedy dodajemy referencję do assembly to informacja o referencji jest dodawana do pliku manifestu assembly. Manifest jest osadzony w assembly jako część metadanych. W pliku manifestu każda referencja jest reprezentowana przez blok przypominający poniższy kod:

// Metadata version: v4.0.30319
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89) // .z\V.4..
.ver 4:0:0:0
}
.assembly extern CommonFunctionality
{
.ver 1:0:0:0
}
.assembly 'Programming CSharp Assembly'
{ … }

Pierwsza linia .assembly extern reprezentuje referencję do assembly mscorlib, która jest dołączana domyślnie przez wszystkie aplikacje .NET ponieważ zawiera definicje wszystkich bazowych typów danych. Wersja assembly to 4:0:0:0 a Public Key Token to (B7 7A 5C 56 19 34 E0 89). Druga .assembly extern reprezentuje referencję do assembly CommonFunctionality w wersji 1.0.0.0, która nie jest podpisana. 
Gdyby druga assembly była podpisana to wpis mógłby wyglądać tak:

// Metadata version: v4.0.30319
.assembly extern mscorlib
{
.publickeytoken = (B7 7A 5C 56 19 34 E0 89 ) // .z\V.4..
.ver 4:0:0:0
}
.assembly extern CommonFunctionality
{
.publickeytoken = (48 27 85 37 58 E3 97 63 ) // H'.7X..c
.ver 1:0:0:0
}
.assembly 'Programming CSharp Assembly'
{ …}
Te informacje można podejrzeć używając narzędzia Intermediate Language Disassembler (ildasm.exe) z linii komend Developer Command Prompt. Uruchamiamy ją podając jako parametr ścieżkę i nazwę pliku assembly. Otwiera to okno aplikacji w którym podglądamy manifest:
man

Kiedy uruchamiamy aplikację to środowisko uruchomieniowe próbuje zlokalizować wszystkie wymagane assembly. 
Jeżeli assembly nie jest podpisane to CLR szuka w lokalnym folderze aplikacji bazując na nazwie assembly ignorując wersję. 
Jeżeli assembly jest podpisana to CLR pierw sprawdza czy są jakieś zasady określone dla danego assembly które mogą wskazać, że trzeba użyć innej wersji assembly. Proces zastępowania assembly bez konieczności rekompilacji innych assembly które jej używają nazywa się binding redirection. Kiedy wersja jest ustalona, CLR pierw przeszukuje GAC w poszukiwaniu określonej wersji assembly, jeżeli tam nie znajdzie to szuka w lokalnym folderze aplikacji. Kiedy znajdzie assembly to sprawdza jej podpis cyfrowy. Jeżeli CLR znejdzie assembly ale nie znajdzie odpowiedniej jej wersji lub kiedy podpis się nie zgadza to zgłoszony zostaje wyjątek System.IO.FileLoadException. Jeżeli assembly nie może być znalezione to zgłoszony zostaje wyjątek System.IO.FileNotFoundException.

Od wersji .NET 2.0Microsoft dodał do nazwy assembly architekturę procesora, lecz jest ona opcjonalna. Oznacza to, że można mieć dwie takie same wersje assembly rozróżnione atrybutem ProcessorArchitecture
Wartości atrybutu ProcessorArchitecture są opisane enumeracją System.Reflection.ProcessorArchitecture:

  • None - niewymieniona lub nieznana.
  • MSIL - niezależne od procesora.
  • x86 - 32-bitowy procesor Intel.
  • IA64 - 64-bitowy procesor Intel.
  • Amd64 - 64-bitowy procesor AMD.
  • Arm - procesor ARM.

Architektura procesora jest wyświetlana tylko jeżeli jest różna od None
Przykładowo jeżeli assembly System miałoby ustawiony atrybut ProcessorArchitecture do MSIL to nazwa assembly byłaby następująca:

System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089, ProcessorArchitecture=MSIL

Dodawanie assembly do GAC

Jeżeli aplikacja nie jest aplikacją .NET i korzysta z bibliotek dołączanych dynamicznie, to poszukuje ich w folderze instalacji aplikacji lub w folderach określonych w zmiennej środowiskowej PATH
Zamiast tego aplikacje .NET używają GAC, która jest repozytorium różnych podpisanych assembly i która działa jako pamięć podręczna dla bibliotek współdzielonych. Lokalizacja GAC to < Your Windows Installation Folder>\assembly
Zaletami GAC są: współdzieleni assembly, wersjonowanie obok siebie, sprawdzanie podpisu assembly przed instalacją w GAC oraz możliwość do prekompilacji assembly dzięki czemu assembly nie musi być kompilowane przez kompilator JIT za każdym razem gdy jest ładowana. Aby dokonać prekompilacji należy użyć narzędzia ngen.exe (Native Image Generator).

Assembly można dodać do GAC na dwa sposoby. 
Pierwszym jest użycie instalatora. jest to preferowany sposób. Używając instalatora upewniamy się, że instalacja jest atomowa. 
Drugim sposobem jest użycie narzędzia gacutil.exe, które może być użyte w następujący sposób z linii komend:

gacutil.exe[options][assemblyName | assemblyPath | assemblyListFile]

Najczęściej wykorzystywane parametry gacutil.exe:

  • /i - dodaje assembly do GAC.
  • /u - usuwa assembly do GAC.
  • /l [assemblyName] - wyświetla wszystkie assembly w GAC. Dodanie assemblyName filtruje listę do assembly o określonej nazwie.
  • /r - śledzi referencje do assembly poprzez zwiększanie i zmniejszanie licznika w czasie instalacji i deinstalacji.

Podsumowanie

Wybór algorytmu szyfrowania

  • Do szyfrowania danych używanych lokalnie lub jeżeli posiadamy bezpieczny sposób przesyłania klucza szyfrowania to używamy szyfrowania symetrycznego.
  • Jeżeli nie posiadamy bezpiecznego sposobu przesyłania klucza szyfrowania pomiędzy partnerami to rekomendowane jest szyfrowanie asymetryczne.
  • Jeżeli wymagana jest tylko integralność danych to należy użyć algorytmu skrótu.
  • Jeżeli wymagana jest integralność danych i autentyczność należy użyć algorytmu MAC.

Szyfrowanie symetryczne

  • Bazuje na podstawie wspólnego klucza szyfrowania.
  • Wymaga wektora inicjującego IV, który nie musi być tajny lecz jest używany do szyfrowania pierwszego bloku danych.
  • Aby go użyć tworzymy instancję obiektu algorytmu symetrycznego po czym wywołujemy metodę CreateEncryptor lub CreateDecryptor.
  • Obiekt szyfrujący lub deszyfrujący jest potem używany z metodą TransformFinalBlock lub poprzez wysłanie do strumienia CryptoStream.

Szyfrowanie asymetryczne

  • Bazuje na podstawie pary komplementarnych kluczy. Dane zaszyfrowane jednym z nich mogą być odszyfrowane jedynie drugim.
  • Jeden z kluczy jest nazywany kluczem prywatnym i jest atjny. Drugi z kluczy nazywany jest kluczem publicznym i jest dostępny dla osób szyfrujących dane lub chcących sprawdzić zaszyfrowane dane.

Funkcja skrótu

  • Skrót jest procesem mapowania danych binarnych o zmiennej długości na dane binarne określonej długości.
  • kiedy chcemy mieć pewność, że dane nie zostały zmodyfikowane podczas transportu to możemy strwożyć ich skrót przed wysłaniem i przesłać razem z danymi w celu weryfikacji zgodności.
  • Dwa powszechnie stosowane algorytmy to SHA256 i SHA512.

Zarządzanie kluczami

  • klucze symetryczne mogą być wymieniane używając algorytmów asymetrycznych.
  • Asymetryczne klucze prywatne mogą być zabezpieczone używając certyfikatu lub kontenera Crypto Service Providers.

Wersja assembly

  • Wersja assembly składa się z czterech części: MajorMinorBuild i Revision.

Silne nazwy

  • Assembly podpisane cyfrowo jest nazywane assembly o silnej nazwie.
  • Silna nazwa posiada pięć części: Friendly NameVersionCulturePublic Key Token i Processor Architecture.

GAC

  • Rozwinięcie skrótu to Global Assembly Cache.
  • GAC jest repozytorium współdzielonych assembly w .Net.
  • Do GAC mogą być wdrażane tylko assembly z silną nazwą.
  • GAC może się znajdować więcej niż jedna wersja tej samej assembly.