Een niet-ondertekend installatieprogramma is het eerste wat er bij een klant misgaat. SmartScreen waarschuwt voor een onbekende uitgever, de UAC-prompt toont geen naam, en een MSIX-pakket installeert helemaal niet, omdat Windows alleen een pakket installeert dat een geldige handtekening draagt.
sgcSign 2026.10 voegt hiervoor twee componenten toe. TsgcMSISigner ondertekent Windows Installer-bestanden .msi en .msp, en TsgcAppxSigner ondertekent .msix- en .appx-pakketten en hun bundels. Beide werken precies zoals de Authenticode-ondertekenaar die u wellicht al gebruikt voor een EXE, een sleutelprovider, een optionele tijdstempelautoriteit, één aanroep.
Een MSI en een MSIX ondertekend vanuit het Delphi-programma, en de uitgeverscontrole die een pakket tegenhoudt dat toch nooit zou installeren. Ook op YouTube.
Het programma
Eén consoleprogramma dat de ondertekenaar kiest op basis van de bestandsextensie, ondertekent met SHA-256 en, wanneer een tijdstempelautoriteit is opgegeven, een RFC 3161-tijdstempel toevoegt.
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 ondertekenaars stammen af van TsgcAuthenticodeSigner, dus de sleutelprovider, de digest, het tijdstempelniveau en de beschrijving worden ingesteld op de gemeenschappelijke basis, precies zoals bij een EXE, en SignFile kan zowel via het basistype als op de concrete klasse worden aangeroepen.
Een Windows Installer-pakket ondertekenen
Een .msi is geen PE-bestand. Het is een compound file, een klein bestandssysteem van streams en storages, en de digest die Windows controleert wordt berekend over die streams in een precieze volgorde, waarbij de handtekeningstreams worden weggelaten. TsgcMSISigner berekent die digest, bouwt de PKCS#7-handtekening en schrijft deze naar de DigitalSignature-stream van het pakket. SHA-1, SHA-256, SHA-384 en SHA-512 worden allemaal geaccepteerd, en een RFC 3161-tijdstempel houdt de handtekening geldig nadat het certificaat is verlopen.
Patchpakketten (.msp) worden op dezelfde manier ondertekend. Met AppendSignature ingesteld, krijgt een pakket dat al ondertekend is een tweede handtekening naast de eerste, zoals signtool /as er een toevoegt aan een EXE.
Een MSIX- of APPX-pakket ondertekenen
Een MSIX is een ZIP met een blokkaart, en wat Windows ondertekent is niet een hash van het bestand maar een kleine structuur van digests over de onderdelen ervan. TsgcAppxSigner bouwt die structuur, ondertekent deze en schrijft het resultaat in het pakket als AppxSignature.p7x. Als het pakket het handtekeningdeel nog niet declareert, wordt het toegevoegd. Windows accepteert voor een pakket alleen SHA-256, dus dat is de enige aangeboden digest, en het opnieuw ondertekenen van een reeds ondertekend pakket vervangt de oude handtekening.
De uitgeverscontrole
Dit is de fout die u een middag kost. De Publisher in het pakketmanifest moet exact overeenkomen met het subject van het ondertekeningscertificaat. Wanneer de twee verschillen, meldt Windows geen ongeldige handtekening. Het herkent het pakket niet meer en noemt het een bestandsformaat dat het niet kan verifiëren, wat eruitziet als een beschadigde build.
TsgcAppxSigner controleert dit voordat het ondertekent, en stopt met een melding die beide waarden noemt.
> 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.
De vergelijking gebeurt op basis van attribuuttype en waarde, niet als string, zodat verschillen in spatiëring en volgorde tussen het manifest en het certificaat geen vals alarm veroorzaken. ValidatePublisher (standaard True) schakelt de controle uit, en met sgcAppxReadPublisher en sgcAppxDNMatches kunt u dezelfde vergelijking zelf uitvoeren, bijvoorbeeld om een build te laten mislukken voordat deze de ondertekeningsstap bereikt.
Het resultaat controleren
> 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 vindt op Windows dezelfde ondertekenaar en de DigiCert-tijdstempel op beide bestanden, en de digest die het berekent is degene die werd ondertekend. De enige klacht is 0x800B010A, omdat de demoketen eindigt in een zelfondertekende testroot die niet op de machine is geïnstalleerd. Een beschadigde digest of handtekening zou in plaats daarvan 0x80096010 zijn. TsgcAuthenticodeVerifier herkent een installatiepakket en een MSIX-pakket vanzelf, zodat dezelfde aanroep die een EXE verifieert, ook deze verifieert.
Op de server en de opdrachtregel
De sgcSign Server ondertekent beide formaten via /api/v1/sign/msi en /api/v1/sign/appx, en het opdrachtregelprogramma gebruikt --format msi en --format appx. Beide hebben ook een route die alleen de hash verwerkt, zodat een groot installatieprogramma nooit over het netwerk gaat, wat het onderwerp is van de volgende post in deze reeks.
Beschikbaarheid
TsgcMSISigner en TsgcAppxSigner worden geleverd in sgcSign 2026.10 voor Delphi en C++Builder, samen met de serverroutes en de opdrachtregelformaten. Ze bouwen en draaien op Linux, macOS, iOS en Android, naast Windows. Elke eigenschap is gedocumenteerd in de online help van sgcSign.
Vragen over een pakket dat niet wil ondertekenen of niet wil installeren? Neem contact op, en u krijgt antwoord van de mensen die de code hebben geschreven.
