Un installateur non signé est la première chose qu'un client voit se déliter. SmartScreen avertit d'un éditeur inconnu, l'invite UAC n'affiche aucun nom, et un paquet MSIX ne s'installera pas du tout, parce que Windows n'installe qu'un paquet qui porte une signature valide.
sgcSign 2026.10 ajoute deux composants pour cela. TsgcMSISigner signe les fichiers .msi et .msp de Windows Installer, et TsgcAppxSigner signe les paquets .msix et .appx ainsi que leurs bundles. Les deux fonctionnent exactement comme le signataire Authenticode que vous utilisez peut-être déjà pour un EXE : un fournisseur de clé, une autorité d'horodatage facultative, un seul appel.
Un MSI et un MSIX signés depuis le programme Delphi, et la vérification d'éditeur arrêtant un paquet qui ne se serait jamais installé. Aussi sur YouTube.
Le programme
Un programme console qui choisit le signataire selon l'extension du fichier, signe avec SHA-256 et, lorsqu'une autorité d'horodatage est fournie, ajoute un horodatage RFC 3161.
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.
Les deux signataires descendent de TsgcAuthenticodeSigner, donc le fournisseur de clé, le condensé, le niveau d'horodatage et la description sont définis sur la base commune exactement comme pour un EXE, et SignFile peut être appelé aussi bien via le type de base que sur la classe concrète.
Signer un paquet Windows Installer
Un .msi n'est pas un fichier PE. C'est un fichier composé, un petit système de fichiers de flux et de stockages, et le condensé que Windows vérifie est calculé sur ces flux dans un ordre précis, en laissant de côté les flux de signature. TsgcMSISigner calcule ce condensé, construit la signature PKCS#7 et l'écrit dans le flux DigitalSignature du paquet. SHA-1, SHA-256, SHA-384 et SHA-512 sont tous acceptés, et un horodatage RFC 3161 garde la signature valide après l'expiration du certificat.
Les paquets de correctif (.msp) sont signés de la même manière. Avec AppendSignature activé, un paquet déjà signé reçoit une seconde signature à côté de la première, de la même façon que signtool /as en ajoute une à un EXE.
Signer un paquet MSIX ou APPX
Un MSIX est un ZIP avec une carte de blocs, et ce que Windows signe n'est pas un hash du fichier, mais une petite structure de condensés portant sur ses parties. TsgcAppxSigner construit cette structure, la signe et écrit le résultat dans le paquet sous le nom AppxSignature.p7x. Si le paquet ne déclare pas encore la partie signature, elle est ajoutée. Windows n'accepte que SHA-256 pour un paquet, c'est donc le seul condensé proposé, et signer un paquet déjà signé remplace l'ancienne signature.
La vérification de l'éditeur
C'est l'erreur qui coûte tout un après-midi. Le Publisher du manifeste du paquet doit correspondre exactement à l'objet du certificat de signature. Lorsque les deux diffèrent, Windows ne signale pas une signature invalide. Il cesse de reconnaître le paquet et le qualifie de format de fichier qu'il ne peut pas vérifier, ce qui ressemble à une build corrompue.
TsgcAppxSigner le vérifie avant de signer et s'arrête avec un message qui nomme les deux valeurs :
> 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.
La comparaison se fait par type et valeur d'attribut, pas comme une chaîne de caractères, si bien que des différences d'espacement ou d'ordre entre le manifeste et le certificat ne provoquent pas de fausse alerte. ValidatePublisher (True par défaut) désactive la vérification, et sgcAppxReadPublisher et sgcAppxDNMatches vous permettent d'exécuter vous-même la même comparaison, par exemple pour faire échouer une build avant qu'elle n'atteigne l'étape de signature.
Vérifier le résultat
> 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 sous Windows trouve le même signataire et l'horodatage DigiCert sur les deux fichiers, et le condensé qu'il calcule est celui qui a été signé. Sa seule plainte est 0x800B010A, parce que la chaîne de démonstration se termine par une racine de test autosignée qui n'est pas installée sur la machine. Un condensé ou une signature corrompus donneraient 0x80096010 à la place. TsgcAuthenticodeVerifier reconnaît de lui-même un paquet d'installation et un paquet MSIX, si bien que le même appel qui vérifie un EXE vérifie aussi ceux-ci.
Sur le serveur et en ligne de commande
Le sgcSign Server signe les deux formats sur /api/v1/sign/msi et /api/v1/sign/appx, et l'outil en ligne de commande accepte --format msi et --format appx. Les deux disposent aussi d'une route uniquement par hash, pour qu'un gros installateur ne traverse jamais le réseau, sujet du prochain article de cette série.
Disponibilité
TsgcMSISigner et TsgcAppxSigner sont livrés dans sgcSign 2026.10 pour Delphi et C++Builder, avec les routes serveur et les formats en ligne de commande. Ils se compilent et s'exécutent sur Linux, macOS, iOS et Android en plus de Windows. Chaque propriété est documentée dans l'aide en ligne de sgcSign.
Des questions sur un paquet qui ne se signe pas ou ne s'installe pas ? Contactez-nous, et vous recevrez une réponse des personnes qui ont écrit le code.
