Ein unsignierter Installer ist das Erste, was einem Kunden als fehlerhaft auffällt. SmartScreen warnt vor einem unbekannten Herausgeber, die UAC-Eingabeaufforderung zeigt keinen Namen an, und ein MSIX-Paket lässt sich überhaupt nicht installieren, weil Windows nur ein Paket installiert, das eine gültige Signatur trägt.
sgcSign 2026.10 fügt dafür zwei Komponenten hinzu. TsgcMSISigner signiert Windows Installer .msi- und .msp-Dateien, und TsgcAppxSigner signiert .msix- und .appx-Pakete sowie deren Bundles. Beide funktionieren genau wie der Authenticode-Signierer, den Sie vielleicht schon für eine EXE verwenden: ein Schlüsselanbieter, eine optionale Zeitstempelbehörde, ein einziger Aufruf.
Eine MSI und eine MSIX, signiert aus dem Delphi-Programm, und die Herausgeberprüfung, die ein Paket stoppt, das sich nie installieren ließe. Auch auf YouTube.
Das Programm
Ein Konsolenprogramm, das den Signierer anhand der Dateierweiterung auswählt, mit SHA-256 signiert und, wenn eine Zeitstempelbehörde angegeben ist, einen RFC 3161-Zeitstempel hinzufügt.
program SignInstaller;
{$APPTYPE CONSOLE}
uses
SysUtils, StrUtils, sgcSign_Authenticode, sgcSign_MSI, sgcSign_APPX,
sgcSign_KeyProvider_PFX, sgcSign_TSA;
var
PFX: TsgcPFXKeyProvider;
TSA: TsgcTSAClient;
Signer: TsgcAuthenticodeSigner;
begin
if ParamCount < 4 then
begin
WriteLn('usage: SignInstaller <input.msi|input.msix> <output> ' +
'<file.pfx> <password> [tsa-url]');
Halt(1);
end;
if MatchText(ExtractFileExt(ParamStr(1)), ['.msi', '.msp']) then
Signer := TsgcMSISigner.Create(nil)
else
Signer := TsgcAppxSigner.Create(nil); // .msix .appx and bundles
PFX := TsgcPFXKeyProvider.Create(nil);
TSA := TsgcTSAClient.Create(nil);
try
try
PFX.LoadFromFile(ParamStr(3), ParamStr(4));
Signer.KeyProvider := PFX;
Signer.Hash := ahSHA256;
Signer.Level := alBES;
if ParamCount > 4 then
begin
TSA.URL := ParamStr(5);
Signer.TSAClient := TSA;
Signer.Level := alT;
end;
if Signer is TsgcAppxSigner then
begin
WriteLn('Publisher : ', sgcAppxReadPublisher(ParamStr(1)));
WriteLn('Certificate: ', PFX.Certificate.SubjectRFC2253);
TsgcAppxSigner(Signer).SignFile(ParamStr(1), ParamStr(2));
end
else
TsgcMSISigner(Signer).SignFile(ParamStr(1), ParamStr(2));
WriteLn('Signed: ', ParamStr(2));
except
on E: Exception do
begin
WriteLn('Error: ', E.Message);
ExitCode := 1;
end;
end;
finally
Signer.Free;
TSA.Free;
PFX.Free;
end;
end.
Beide Signierer stammen von TsgcAuthenticodeSigner ab, daher werden der Schlüsselanbieter, der Digest, die Zeitstempelstufe und die Beschreibung auf der gemeinsamen Basisklasse gesetzt, genau wie bei einer EXE, und SignFile kann sowohl über den Basistyp als auch auf der konkreten Klasse aufgerufen werden.
Signieren eines Windows-Installer-Pakets
Eine .msi ist keine PE-Datei. Sie ist eine Compound-Datei, ein kleines Dateisystem aus Streams und Storages, und der Digest, den Windows prüft, wird über diese Streams in einer genauen Reihenfolge berechnet, wobei die Signatur-Streams ausgelassen werden. TsgcMSISigner berechnet diesen Digest, erstellt die PKCS#7-Signatur und schreibt sie in den DigitalSignature-Stream des Pakets. SHA-1, SHA-256, SHA-384 und SHA-512 werden alle akzeptiert, und ein RFC 3161-Zeitstempel hält die Signatur gültig, nachdem das Zertifikat abgelaufen ist.
Patch-Pakete (.msp) werden auf die gleiche Weise signiert. Ist AppendSignature gesetzt, erhält ein bereits signiertes Paket eine zweite Signatur neben der ersten, so wie signtool /as einer EXE eine weitere hinzufügt.
Signieren eines MSIX- oder APPX-Pakets
Eine MSIX ist ein ZIP mit einer Blockkarte, und was Windows signiert, ist kein Hash der Datei, sondern eine kleine Struktur aus Digests über ihre Teile. TsgcAppxSigner erstellt diese Struktur, signiert sie und schreibt das Ergebnis als AppxSignature.p7x in das Paket. Wenn das Paket den Signaturteil noch nicht deklariert, wird er hinzugefügt. Windows akzeptiert für ein Paket nur SHA-256, das ist also der einzige angebotene Digest, und das Signieren eines bereits signierten Pakets ersetzt die alte Signatur.
Die Herausgeberprüfung
Das ist der Fehler, der einen ganzen Nachmittag kostet. Der Publisher im Paketmanifest muss genau dem Antragsteller des Signaturzertifikats entsprechen. Wenn die beiden voneinander abweichen, meldet Windows keine ungültige Signatur. Es erkennt das Paket einfach nicht mehr und bezeichnet es als ein Dateiformat, das es nicht prüfen kann, was wie ein beschädigter Build aussieht.
TsgcAppxSigner prüft das vor dem Signieren und bricht mit einer Meldung ab, die beide Werte nennt:
> SignInstaller.exe DemoApp-wrongpublisher.msix out.msix ..\certs\demo.pfx demo
Publisher : CN=Someone Else
Certificate: CN=sgcSign Demo Code Signing,O=eSeGeCe Demo
Error: APPX: the signing certificate subject "O=eSeGeCe Demo, CN=sgcSign Demo Code Signing"
does not match the Publisher declared in AppxManifest.xml ("CN=Someone Else"). Windows
requires them to be identical and refuses a package where they differ; signtool rejects
the same combination with 0x8007000B. Sign with a certificate whose subject matches the
manifest, or rebuild the package with the Publisher set to the certificate subject.
Der Vergleich erfolgt nach Attributtyp und Wert, nicht als Zeichenkette, sodass Abstands- und Reihenfolgeunterschiede zwischen Manifest und Zertifikat keinen Fehlalarm auslösen. ValidatePublisher (standardmäßig True) schaltet die Prüfung ab, und sgcAppxReadPublisher sowie sgcAppxDNMatches lassen Sie denselben Vergleich selbst durchführen, zum Beispiel um einen Build schon vor dem Signierschritt fehlschlagen zu lassen.
Das Ergebnis prüfen
> SignInstaller.exe DemoApp.msi DemoApp-signed.msi ..\certs\demo.pfx demo http://timestamp.digicert.com
Signed: DemoApp-signed.msi
> SignInstaller.exe DemoApp.msix DemoApp-signed.msix ..\certs\demo.pfx demo http://timestamp.digicert.com
Publisher : CN=sgcSign Demo Code Signing, O=eSeGeCe Demo
Certificate: CN=sgcSign Demo Code Signing,O=eSeGeCe Demo
Signed: DemoApp-signed.msix
> Get-AuthenticodeSignature DemoApp-signed.msi, DemoApp-signed.msix |
Format-List Path, @{n='Signer';e={$_.SignerCertificate.Subject}},
@{n='TSA';e={$_.TimeStamperCertificate.Subject}}
Path : DemoApp-signed.msi
Signer : CN=sgcSign Demo Code Signing, O=eSeGeCe Demo
TSA : CN=DigiCert SHA256 RSA4096 Timestamp Responder 2026 1, O="DigiCert, Inc.", C=US
Path : DemoApp-signed.msix
Signer : CN=sgcSign Demo Code Signing, O=eSeGeCe Demo
TSA : CN=DigiCert SHA256 RSA4096 Timestamp Responder 2026 1, O="DigiCert, Inc.", C=US
signtool verify /pa /v findet unter Windows auf beiden Dateien denselben Signierer und den DigiCert-Zeitstempel, und der berechnete Digest ist der, der signiert wurde. Die einzige Beanstandung ist 0x800B010A, weil die Demo-Kette in einem selbstsignierten Test-Stammzertifikat endet, das auf dem Rechner nicht installiert ist. Ein beschädigter Digest oder eine beschädigte Signatur wäre stattdessen 0x80096010. TsgcAuthenticodeVerifier erkennt ein Installer-Paket und ein MSIX-Paket von selbst, sodass derselbe Aufruf, der eine EXE prüft, auch diese prüft.
Auf dem Server und in der Befehlszeile
Der sgcSign Server signiert beide Formate unter /api/v1/sign/msi und /api/v1/sign/appx, und das Kommandozeilentool nimmt --format msi und --format appx entgegen. Beide haben außerdem eine reine Hash-Route, sodass ein großer Installer nie über das Netzwerk geht, das Thema des nächsten Beitrags dieser Serie.
Verfügbarkeit
TsgcMSISigner und TsgcAppxSigner sind ab sgcSign 2026.10 für Delphi und C++Builder verfügbar, zusammen mit den Serverrouten und den Befehlszeilenformaten. Sie lassen sich neben Windows auch unter Linux, macOS, iOS und Android erstellen und ausführen. Jede Eigenschaft ist in der sgcSign Online-Hilfe dokumentiert.
Fragen zu einem Paket, das sich nicht signieren oder nicht installieren lässt? Nehmen Sie Kontakt auf, und Sie erhalten eine Antwort von den Leuten, die den Code geschrieben haben.
