FireMonkey Apps für iOS und Android werden in Delphi im Rahmen des Bereitstellungsprozesses automatisch signiert – dies ist inbesondere für iOS Apps unabdingbar und wurde daher in der Delphi IDE auch entsprechend umgesetzt.
Bei Windows Anwendungen (egal ob 32 oder 64 Bit) ist dagegen keine Signatur-Option in Delphi zu finden. Dies dürfte dazu führen, dass viele Delphi Apps unsigniert verteilt werden. Dies ist jedoch spätestens seit Windows 10 nicht mehr zu empfehlen, denn der mit Windows 10 eingeführte „SmartScreen-Filter“ blockiert standardmäßig „nicht vertrauenswürdige“ Anwendungen. Eine unsignierte Anwendung (EXE) ist per se „nicht vertrauenswürdig“, da ihre Herkunft nicht verifiziert werden kann.
Hin und wieder findet man dann den wenig schlauen „Tip“, wie man diese SmartScreen Prüfung abschalten kann. Einzig richtige Vorgehensweise ist es dagegen, als Entwickler nur signierte Anwendungen heraus zu geben (egal ob öffentlich oder inhouse)! Als Anwender sollte man Lieferanten auf die Füße treten, die weiterhin unsignierte Anwendungen verkaufen.
Signatur-Zertifikate
Um eine EXE zu signieren, ist zunächst ist ein valides Zertifikat erforderlich, welches das sog. Code Sigining unterstützt. Solche Zertifikate werden von Anbietern wie Comodo, Thawte, Symantec und anderen herausgegeben. Die Anbieter führen dabei je nach gewählter Preisklasse eine mehr oder minder intensive Indentitätsprüfung des Beantragenden durch – schließlich soll das Zertifikat ja eindeutig die Firma oder Person, die eine Anwendung liefert, identifizieren können. Die Kosten liegen dabei jährlich im unteren bis mittleren dreistelligen Bereich.
Brauchbare kostenlose Zertifikate konnte ich nicht finden (so scheut z.B. LetsEncrypt den Aufwand, den eine Identitätsprüfung mit sich bringt), die günstigste Variante ist ein Comodo-Zertifikat per CodeSignCert zu bestellen. Dort ist man mit $75 für ein Jahr dabei.
UPDATE 08.03.2020:
Ich habe kürzlich ein neues Zertifikat bestellt. Auf der bereits oben erwähnten Seite von CodeSignCert habe ich dabei die „SECTIGO CODE SIGNING“ Variante für $75/Jahr ausgewählt. Dies hat einwandfrei funktioniert.
Signieren in Delphi
Zunächst ist das entsprechende Windows 10 SDK zu installieren, welches die sog. „signtool.exe“ von Microsoft enthält. Die 32-Bit Version wird sich danach typischerweise in einem Pfad ähnlich diesem finden : C:\Program Files\Windows Kits\10\bin\x86\signtool.exe
Nun ist zu überlegen, wie mit dem Zertifikat umgegangen werden soll. Es gibt zwei Möglichkeiten:
- Installation im Windows-Zertifikatspeicher und damit Nutzung des Windows-eigenen Schutzmechanismusses.
- Speichern des Zertifikats als PFX-Datei und Hinterlegen des Passworts im Build-Prozess der Anwendung.
Letztlich muss bedacht werden, dass dieses Zertifikat wie ein Personalausweis zu verstehen ist. Man sollte es also nicht einfach „irgendwo rumliegen“ lassen, da sonnst jedermann diese Identität für seine eigenen Zwecke nutzen könnte. Besonder das Einchecken in ein Sourcecontrol-System scheint hier wenig sinnvoll.
Ich nutze in der Regel den Windows-Zertifikatspeicher, dort habe ich dann eine zentrale Übersicht meiner Zertifikate und kann so auch schnell sehen, wann ein Zertifikat abläuft.
Grundsätzlich kann man nun eine kompilierte Delphi-Anwendung von Hand signieren, indem man folgenden Befehl auf der Kommandozeile eingibt:
"C:\Program Files\Windows Kits\10\bin\x86\signtool.exe" sign /n "developer experts" /tr http://timestamp.comodoca.com /du https://www.developer-experts.net C:\Projekte\SignTest\Win32\Release\SignTest.exe
Die SignTest.exe wird dadurch mit einem Zertifikat signiert, welches im „Subject“ (dem Namen) „Developer Experts“ enhält. Wenn sich nur ein einziges Code Signing Zertifikat im Windows Zertifikatspeicher befindet, dann kann man die Option /n auch weg lassen – es schadet aber auch nicht.
Die weiteren Optionen:
/tr – hier wird ein sog. Timestam-Server angegeben. Verschiedene Zertifikatslieferanten (die sog. CAs) betreiben diese, ich nutze hier den Server von Comodo – es muss jedoch nicht zwingend der Server des eigenen Zertifikatslieferanten sein. Durch diese Option wird (kurz formuliert) dem Timestamp-Server ein Hash meiner Exe übermittelt, der Server stellt dann eine Signatur zu diesem Hash aus, die mit einem, auf dem Server generierten, Zeitstempel versehen wird. Dies wird dann der eigentlichen Signatur hinzugefügt. Das Verfahren soll verhindern, dass der Lieferant einer EXE nicht „schummeln“ kann, also nicht vor- oder rückdatieren kann.
Achtung: Verschiedene ältere Blogeinträge zu diesem Thema nutzen meinst nur die Option /t – dies führt jedoch – ohne weitere Angabe eines Algorithmus – zur Nutzung des veralteten SHA1 Verfahrens. Die Option /tr dagegen arbeitet nach RFC 3161 und handelt mit dem Server mindestens SHA256 aus.
/du – fügt die angegebene URL als „erweiterte Beschreibung“ der Signatur hinzu. Dies ist insbesondere für eine Hersteller-URL geeignet
Um diesen Signiervorgang nicht jedes mal manuell ausführen zu müssen, kann man das in die Post-Build-Option des jeweiligen Delphi Projekts einbinden. Ich nutze hierfür die Release-Konfiguration, weil nur diese zum Kunden geht.
Zu beachten ist, dass man hier praktischerweise „($OUTPUTPATH)“ als Signaturziel angibt, damit der Pfad nicht hartkodiert ist – und man das ganze per Copy and Paste in andere Projekte einfügen kann 😉
Ausserdem ist das Pre-Build-Ereignis „del ($OUTPUTPATH)“ wichtig, weil sonst mitunter das Signieren fehlschlägt.
UPDATE 08.03.2020:
Zumindest auf 64-bit Windows 10 Versionen scheint sich zwischenzeitlich der Standard-Pfad des SignTools veändert zu haben, so dass der gesamte Befehl nun so aussieht:
"C:\Program Files (x86)\Windows Kits\10\App Certification Kit\signtool.exe" sign /n "NAME DES ZERTIFIKATS ('Ausgestellt für')" /tr http://timestamp.comodoca.com /du https://www.developer-experts.net "$(OUTPUTPATH)"
Mit jedem „Erzeugen“ wird nun in der Release-Konfiguration die EXE signiert – die Debug-Konfiguration bleibt unverändert. Gerade bei sehr großen Projekten kann eine Debug-Exe schon mal schnell mehrere hundert MB groß werden, was beim Signieren dann auch ein wenig Zeit benötigt.