In diesem Post werde ich kurz auf das Konzept der „starken Namen“ im .NET-Framework eingehen. Was ist das und wie versehe ich eine Assembly mit einem solchen?

Was ist ein Strong Name?

Ein Strong Name dient der eindeutigen Identifizierung einer Assembly. Stellen wir uns vor, wir erstellen eine Assembly (z.B. eine Klassenbibliothek) ohne sie mit einem Strong Name zu versehen, die dann über ihren Namen und ihre Version identifiziert wird. Ein zweiter Entwickler erstellt auf der anderen Seite der Welt gerade ebenfalls eine Assembly, zufällig mit dem gleichen Namen und derselben Version. Beide Assemblies werden an den Kunden ausgeliefert, bei diesem gibt es nun also zwei Assemblies, die in Namen und Version übereinstimmen aber völlig unterschiedliche Inhalte haben. Solange beide Assemblies im jeweiligen Anwendungsverzeichnis liegen stellt das noch kein Problem dar, sollen die Assemblies aber im Global Assembly Cache (GAC) installiert werden, kommt es zu einem Konflikt. Für den GAC muss eine Assembly also eindeutig identifizierbar sein und genau dazu wird das Konzept des Strong Name genutzt. Dabei wird eine Assembly mit einem digitalen Zertifikat versehen, wodurch sie über ihren Namen, einen öffentlichen Schlüssel und das Zertifikat eindeutig identifizierbar ist. Eine parallele Installation zweier Assemblies mit demselben Namen im GAC ist dann möglich, da sich der öffentliche Schlüssel und das Zertifikat unterscheiden. Durch das Zertifikat kann außerdem sichergestellt werden, dass die Assembly auf dem Weg vom Entwickler zum Kunden nicht manipuliert wurde, dazu aber später mehr.

Versehen einer Assembly mit einem Strong Name

Um eine Assembly mit einem Strong Name zu versehen wird zunächst eine Schlüsselpaar (Public und Private Key) benötigt. Visual Studio bzw. das Windows SDK bringt das benötigte Tool zum Erstellen eines solchen bereits mit. Um es nutzen zu können muss die Visual Studio Developer-Eingabeaufforderung gestartet werden (unter Windows 8.1 versteckt sich diese in der App-Übersicht unter Microsoft Visual Studio 2012 → Developer-Eingabeaufforderung für VS2012 bzw. Visual StudioVisual Studio-ToolsDeveloper-Eingabeaufforderung für VS2013). Darin kann durch Ausführen des folgenden Befehls ein Schlüsselpaar aus privatem und öffentlichen Schlüssel erzeugt werden:

sn -k KeyFile.snk

Dadurch wird die Datei KeyFile.snk erzeugt, welche ein neues Schlüsselpaar enthält. Diese Datei kann nun verwendet werden, um einer Assembly einen Strong Name zu verpassen. Dazu werden in Visual Studio die Projekteigenschaften geöffnet und zum Reiter „Signierung“ gewechselt, wo die Checkbox „Assembly signieren“ aktiviert wird. Unter „Schlüsseldatei mit starkem Namen auswählen“ wird die soeben erstellte Schlüsseldatei gewählt. Das sollte dann etwa so aussehen:

Angabe der Schlüsseldatei in den Projektoptionen

Angabe der Schlüsseldatei in den Projektoptionen

Die Schlüsseldatei wird dabei automatisch zum Projekt hinzugefügt. Nachdem dieses gespeichert und neu erstellt wurde, verfügt die Assembly über einen Strong Name. Öffnet man die Assembly in einem Decompiler wie z.B. dotPeek sieht man auch den öffentlichen Schlüssel, der der Assembly hinzugefügt wurde:

Die Assembly verfügt über einen öffentlichen Schlüssel

Die Assembly verfügt über einen öffentlichen Schlüssel

Auf die oben erläuterte Weise kann eine Assembly also mit einem Strong Name signiert werden. Diese Methode hat jedoch den Nachteil, dass das Schlüsselpaar inklusive privatem Schlüssel dem Projekt und dem hoffentlich verwendeten Source Code Management-System hinzugefügt wird. Solange man den Quellcode nicht veröffentlicht oder in irgendeiner Weise weitergibt ist dies auch kein Problem, wird dies aber gemacht gibt man so auch den privaten Schlüssel weiter. Jeder kann dann die Assembly verändern und es ist nicht mehr feststellbar, ob eine Assembly tatsächlich vom Originalautor stammt. Besser wäre es also, wenn die Assembly erst beim Release signiert wird. Eine nachträgliche Signierung einer bereits kompilierten Assembly ist aber nicht ohne Weiteres möglich, dies muss während des Kompilierens geschehen. Muss der private Schlüssel also immer dem Quellcode beiliegen? Nein, denn dafür gibt es das sogenannte „verzögerte Signieren“ (Delayed Signing). Dabei wird dem Quellcode nur der öffentliche, nicht aber der private Schlüssel beigelegt. Bei der Kompilierung wird dann nur der öffentliche Schlüssel zur Signierung verwendet, während für die vollständige Signierung mit Hilfe des privaten Schlüssels nur der dafür notwendige Platz in der Assembly reserviert wird. Diese vollständige Signierung kann dann später, vor dem Release, mittels einem Kommandozeilentool nachgeholt werden.
Auf diese Weise kann die Assembly mit einem Strong Name signiert werden, der private Schlüssel aber abseits des eigentlichen Quellcodes unter Verschluss gehalten werden. Die folgenden Schritte zeigen, wie das verzögerte Signieren durchgeführt wird.

Zunächst muss der öffentliche Schlüssel aus der erstellten Schlüsselpaar-Datei extrahiert werden. Dies kann in der Visual Studio Developer-Eingabeaufforderung wieder mit dem Tool Sn.exe durchgeführt werden:

sn -p KeyFile.snk PublicKeyFile.snk

Dem Tool wird also die bereits erstellte Schlüsselpaar-Datei (siehe oben) übergeben und der Name der Datei, in der der öffentliche Schlüssel gespeichert werden soll. Diese Datei PublicKeyFile.snk enthält nun nur noch eben diesen öffentlichen Schlüssel, der private Schlüssel ist  nicht mehr enthalten.
In Visual Studio wird in den Projekteigenschaften nun diese neue Schlüsseldatei für die Signierung angegeben. Außerdem wird die Checkbox „Nur signieren verzögern“ aktiviert:

Projektoptionen für verzögertes Signieren

Projektoptionen für verzögertes Signieren

Der Hinweis auf der Konfigurationsseite weist bereits darauf hin, dass eine so konfigurierte Assembly nicht ausgeführt oder debuggt werden kann. Wird das Projekt gespeichert und gestartet bewahrheitet sich dies auch:

Fehler beim Starten einer verzögert signierten Assembly

Fehler beim Starten einer verzögert signierten Assembly

Dies liegt daran, dass das .NET-Framework versucht, die Signierung der Assembly zu prüfen, was natürlich fehlschlägt. Auf den Entwicklungsrechnern muss diese Überprüfung daher deaktiviert werden. Auch dies kann wieder durch das Kommandozeilentool Sn.exe in der Visual Studio Developer-Eingabeaufforderung (gestartet mit Administratorrechten) durchgeführt werden:

sn -Vr [Assembly]

Für [Assembly] muss dabei der Pfad der Assembly-Datei angegeben werden. Danach lässt sich wie gewohnt mit der Assembly arbeiten. Die Prüfung kann mittels sn -Vu [Assembly] übrigens wieder aktiviert werden.

Vor dem Release muss die Assembly nun mit dem privaten Schlüssel korrekt signiert werden, dies wird durch den folgenden Befehl bewerkstelligt:

sn -R [Assembly] KeyFile.snk

Als [Assembly] muss wieder die Assembly-Datei angeben werden, außerdem muss die Schlüsselpaar-Datei die den öffentlichen und den privaten Schlüssel enthält angegeben werden. Nach Ausführung des Befehls ist die Assembly nun korrekt signiert und kann veröffentlicht werden.

Durch Verwendung des verzögerten Signierens kann die Assembly also mit einem starken Namen versehen werden, wodurch sie zweifelsfrei identifiziert werden kann, und der private Schlüssel kann außerhalb der Quellcodeverwaltung aufbewahrt werden, so dass nur die dazu berechtigten Personen Zugriff auf ihn haben. Dies stellt sicher, dass die Assembly tatsächlich vom Originalautor stammt. Letzteres stellt aber nicht sicher, dass es sich dabei auch um einen vertrauenswürdigen Autor handelt!