2010
03.18

Większość programistów podczas nauki programowania na platformie .NET omija temat silnych nazw i podpisywania assembly – nic dziwnego, kwestie te nie są najistotniejsze na etapie eksperymentowania z .NETem.
W komercyjnych, profesjonalnych albo bardziej zaawansowanych projektach kwestie te zyskują na znaczeniu. W tym wpisie zajmę się tematyką związaną z Global Assembly Cache, podpisywaniem podzespołów (polska nazwa assembly) i silnymi nazwami.

Po co podpisywać assembly?

Zasadnicza odpowiedź na to pytanie jest prosta: z tych samych powodów, dla których podpisuje się cokolwiek innego. Jeśli podpiszesz swoim kluczem prywatnym wiadomość email, odbiorca będzie mógł sprawdzić, że email rzeczywiście pochodzi od Ciebie; jeśli podpiszesz plik cyfrowo, będziesz mógł sprawdzić, czy nikt nie zmienił jego treści.

Między innymi z tych powodów stosuje się podpisywanie assembly – żeby potwierdzić tożsamość podzespołu.

Pełna tożsamość assembly składa się z 4 części:

  • krótkiej nazwy. Na platformie Windows jest to nazwa pliku bez rozszerzenia, np.:  Microsoft.DirectX.Direct3D;
  • kultury, czyli wersji lokalizacyjnej, np.: en;
  • tokena publicznego klucza podpisu: np.: 31bf3856ad364e35;
  • wersji np:  1.0.2902.0.

lub  w postaci jednego łańcucha:

Microsoft.DirectX.Direct3D, Culture=en, 
PublicKeyToken=31bf3856ad364e35, Version=1.0.2902.0

Dopóki nie podpiszesz swojego podezspołu, część PublicKeyToken pozostaje pusta.

Zmiana wartości jednej z tych 4 części skutkuje zmianą tożsamości biblioteki/assembly. Taki sposób identyfikacji bibliotek rozwiązał stary problem DLL Hell, który polegał na tym, że nowe instalacje nadpisywały starsze wersje jakiejś współdzielonej biblioteki, a wcześniej zainstalowane programy odwoływały się do tej nowej biblioteki próbując wykonać operacji, której implementacja się zmieniła w nowej wersji. Skutkował oto błędnym działaniem programów a czasami i systemu operacyjnego.

.NET Framework częściowo rozwiązuje ten problem za pomocą GAC (Global Assembly Cache), gdzie różne wersje tych samych współdzielonych bibliotek mogą być przechowywane bez przeszkód. Jak zatem .NE T rozróżnia rozmaite wersje bibilotek (np. DirectX for Managed Code)? Ano właśnie za pomocą tych 4-częściowych nazw.

Żeby jednak biblioteka (podzespół) mogła być umieszczona w GAC musi mieć silną nazwę.

Silna nazwa podzespołu – Assembly Strong Name

Silna nazwa (strong name) to owa 4-częściowa nazwa, która zawiera PublicKeyToken plus cyfrowy podpis. Jak powstaje ten podpis?

Żeby go utworzyć potrzeba pary kluczy (publiczny-prywatny) o długości 1024-bitów.  Klucz publiczny jest dołączany do 4-częściowej nazwy podzespołu właśnie jako PublicKeyToken. Taka komplementarna para jest wykorzystywana do szyfrowania za pomocą algorytmu RSA.

Kiedy kompilator jest proszony o cyfrowe podpisanie podzespołu, najpierw oblicza skrót z jego zawartości za pomocą funkcji haszującej. Później ten skrót jest szyfrowany kluczem prywatnym pochodzącym z pary kluczy i umieszczany w podzespole.

Tak powstaje podpisany podzespół z silną nazwą.

Kiedy później zachodzi potrzeba zweryfikować tożsamość danego podzespołu (mówiąc wprost: sprawdzić, czy nikt w nim nie grzebał), wykonywane są następujące kroki:

  • liczony jest skrót z zawartości podzespołu;
  • wyłuskiwany jest publiczny klucz (zapisany w podzespole);
  • klucz ten jest używany do rozszyfrowania skrótu podzespołu utworzonego podczas kompilacji;
  • oba skróty (ten z kompilacji i ten wyliczony w kroku pierwszym) są porównywane;
  • jeśli nie są równe znaczy to, że zawartość podzespołu została zmieniona.

Jak utworzyć silną nazwę i podpisać assembly?

Można to zrobić na kilka sposobów, np. za pomocą Visual Studio lub narzędzia linii komend sn.exe.
Omówię pierwszy sposób.
Wybieramy ustawienia naszego projektu (prawy przycisk na nazwie projektu -> Properties) i przechodzimy do zakładki Signing. Zaznaczamy opcję Sign the assembly.

Można to zrobić na kilka sposobów, np. za pomocą Visual Studio lub narzędzia linii komend sn.exe.

Omówię pierwszy sposób.

Wybieramy ustawienia naszego projektu (prawy przycisk na nazwie projektu > Properties) i przechodzimy do zakładki Signing. Zaznaczamy opcję Sign the assembly.

Podpisywanie podzespołu

Podpisywanie podzespołu

Możemy skorzystać z istniejącego pliku z kluczami, bądź utworzyć nowy. Stwórzmy nowy:

Podpisywanie podzespołu

Podpisywanie podzespołu - tworzenie klucza

Wpisujemy nazwę i hasło, które będzie chroniło nasze klucze przed niepowołanym dostępem.

Klikamy OK., zapisujemy projekt i kompilujemy. I już mamy podpisany podzespół z silną nazwą. Wygenerowany plik z kluczami zachowujemy do późniejszego użycia.

Jak zweryfikować, czy nasz podzespół ma poprawną tożsamość? Możemy użyć narzędzia sn.exe z opcją –v. Opcja –T wyświetla publiczny token.

Sprawdzanie podpisu

Sprawdzanie podpisu

Global Assembly Cache

Jak wspomniałem GAC – Global Assembly Cache (globalna pamięć podręczna podzespołów) jest miejscem składowania współdzielonych bibliotek DLL używany przez programy na platformie .NET. Dzięki GAC możemy przestać martwić się o fizyczne położenie używanych przez nas bibliotek na dysku. Jeśli podzespół został dodany do GAC, CLR zawsze będzie w stanie go znaleźć i załadować. GAC rozwiązuje też słynny problem DLL hell (”piekło DLL“) – umożliwia przechowywanie na jednej maszynie dowolnej liczby wersji danej biblioteki.

Standardowo GAC znajduje się w katalogu C:\Windows\Assembly\GAC.

Podzespoły znajdujące się w GAC są traktowane jako bezpieczne. Aby rzeczywiście tak było, podczas instalacji podzespołu w GAC przeprowadzana jest weryfikacja jego cyfrowego podpisu. A więc w GACu mogą być umieszczone tylko podzespoły z silną nazwą.

Poza tym tylko użytkownicy z grup administratorzy i użytkownicy zaawansowani mogą dodawać podzespoły do GAC.

Dla każdego nowododanego podzespołu tworzony jest katalog o nazwie tożsamej z krótką nazwą podzespołu (np. Microsoft.DirectX.Direct3D) a w nim podkatalogi odpowiadające wersjom – nazwy są tworzone wg wzorca wersja__publiczny token (np. 1.0.2902.0__31bf3856ad364e35).

Jak dodawać swoje podzespołu do GAC? Służy do tego narzędzie GACUTIL.EXE (parametry /i i /il instalują podzespoły; parametry /u i /ul odinstalowują podzespoły)

Jak bardzo jest to bezpieczne?

Krótko mówiąc: nie bardzo. Zmiana zawartości podpisanego assembly nie uniemożliwia jego uruchomienia przez CLR. Można co prawda zweryfikować integralność pliku, ale nie można uniemożliwić jego uruchomienia, zwłaszcza na poziomie pełnego zaufania.

Jeśli chodzi o GAC to integralność podzespołu jest sprawdzana wyłącznie w momencie dodawania go do GACa. Później już nie jest. Osoba z uprawnieniami administratora może więc podmienić zweryfikowany podzespół na niezweryfikowany.

Co więcej, silną nazwę można usunąć lub zamienić, co zostało opisane w tym artykule.

Jak więc zabezpieczyć swój kod, tak żeby nie można było go uruchomić, gdy jego integralność została naruszona? Zajmę się tym w jednym z kolejnych wpisów.

Komentarze są zamknięte.