Een Windows EXE ondertekenen vanuit Linux met Delphi

· Componenten
Een Windows EXE ondertekenen vanuit Linux met Delphi

Een Windows programma ondertekenen betekende altijd een Windows machine. signtool draait alleen op Windows, dus een build pipeline die op Linux compileert, heeft toch ergens een Windows host nodig, alleen om aan het einde een handtekening op het bestand te zetten.

Vanaf sgcSign 2026.10 is die host niet meer nodig. De Authenticode indeling in sgcSign, die het PE bestand hasht en de handtekening opbouwt, is Delphi code in plaats van een aanroep naar de Windows signing API's. Op Windows loopt de sleutelbewerking nog altijd via CNG, en overal elders loopt ze in de eigen cryptografie van sgcSign, zodat de componenten die op Windows een EXE ondertekenen ook op Linux 64 bit bouwen en draaien, en Windows het resulterende bestand aanvaardt als elk ander ondertekend bestand. Dit artikel laat een kort console programma zien dat dit doet, hoe je het voor Linux bouwt, en hoe je het resultaat controleert.

Het Delphi programma ondertekent een EXE op Ubuntu, waarna hetzelfde bestand op Windows wordt gecontroleerd. Ook op YouTube.

Waarom op Linux ondertekenen

Het programma

Een console programma dat het te ondertekenen bestand, het uitvoerbestand, een PFX met wachtwoord en, optioneel, een tijdstempelautoriteit als parameter neemt. Het is dezelfde code op Windows en op Linux.

program SignExe;

{$APPTYPE CONSOLE}

uses
  SysUtils, sgcSign_Authenticode, sgcSign_KeyProvider_PFX, sgcSign_TSA;

var
  PFX: TsgcPFXKeyProvider;
  TSA: TsgcTSAClient;
  Signer: TsgcAuthenticodeSigner;
begin
  if ParamCount < 4 then
  begin
    Writeln('usage: SignExe <input.exe> <output.exe> <file.pfx> <password> [tsa-url]');
    Halt(1);
  end;
  PFX := TsgcPFXKeyProvider.Create(nil);
  TSA := TsgcTSAClient.Create(nil);
  Signer := TsgcAuthenticodeSigner.Create(nil);
  try
    try
      PFX.LoadFromFile(ParamStr(3), ParamStr(4));
      Signer.KeyProvider := PFX;
      Signer.Hash := ahSHA256;
      Signer.Level := alBES;
      Signer.Description := 'sgcSign demo';
      if ParamCount >= 5 then
      begin
        TSA.URL := ParamStr(5);
        Signer.TSAClient := TSA;
        Signer.Level := alT;
      end;
      Signer.SignFile(ParamStr(1), ParamStr(2));
      Writeln('Signer: ', PFX.Certificate.Subject);
      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.

TsgcPFXKeyProvider bevat het certificaat en de private sleutel. TsgcAuthenticodeSigner hasht het PE bestand op dezelfde manier als Windows, bouwt de PKCS#7 handtekening op en schrijft die in de certificaattabel van het bestand. Met een gekoppelde TsgcTSAClient haalt het ook een RFC 3161 tijdstempel op, zodat de handtekening geldig blijft nadat het certificaat is verlopen.

Bouwen voor Linux 64 bit

Voeg in het Projects venster Linux 64-bit toe als doelplatform, met een Linux SDK ingesteld in de SDK Manager, en bouw daarna. De sgcSign runtime packages worden vanaf Delphi 10.3 gebouwd voor Linux64. Op Linux wordt de PFX in pure Pascal geparst, omdat er geen Windows certificaatarchief is om hem in te importeren, en de RSA of ECDSA bewerking loopt in de eigen cryptografie van sgcSign. De handtekeningindeling eromheen wordt opgebouwd door dezelfde code die op Windows draait.

Uitvoeren en het resultaat controleren

$ file Hello.exe
PE32+ executable (console) x86-64, for MS Windows

$ ./SignExe Hello.exe Hello-signed-linux.exe ../certs/demo.pfx demo http://timestamp.digicert.com
Signer: O=eSeGeCe Demo, CN=sgcSign Demo Code Signing
Signed: Hello-signed-linux.exe

Om het zonder Windows te controleren, berekent osslsigncode verify de digest van het bestand opnieuw en controleert de handtekening ervan: Signature verification: ok.

Kopieer het bestand vervolgens naar Windows. PowerShell en signtool verify /pa /v lezen dezelfde handtekening: de bestandshash die Windows berekent komt overeen met de hash die werd ondertekend, de ondertekenaar is het democertificaat, en de DigiCert tijdstempel verifieert. Het bestand draait nog altijd.

PS> Get-AuthenticodeSignature .\Hello-signed-linux.exe | Format-List SignatureType,
      @{n='Signer';e={$_.SignerCertificate.Subject}},
      @{n='TimeStamper';e={$_.TimeStamperCertificate.Subject}}

SignatureType : Authenticode
Signer        : CN=sgcSign Demo Code Signing, O=eSeGeCe Demo
TimeStamper   : CN=DigiCert SHA256 RSA4096 Timestamp Responder 2026 1, O="DigiCert, Inc.", C=US

De enige klacht die Windows heeft, gaat over het democertificaat, niet over de handtekening. De demoketen eindigt in een zelfondertekende testroot die niet op de machine is geïnstalleerd, zodat Windows hem niet kan koppelen aan een root die het vertrouwt. De digest, de handtekening en de tijdstempel verifiëren allemaal.

Dezelfde bytes op Linux en op Windows

Onderteken hetzelfde Hello.exe met dezelfde PFX en zonder tijdstempel, eenmaal met de Linux build en eenmaal met de Windows build van het programma hierboven, en vergelijk de twee bestanden:

Linux    82344 bytes  SHA-256 6FD63BB01A8E911AB099431BC76F3BFA4EF557082BFB8BC18CCBFA97A9E8079D
Windows  82344 bytes  SHA-256 6FD63BB01A8E911AB099431BC76F3BFA4EF557082BFB8BC18CCBFA97A9E8079D

Ze zijn identiek, byte voor byte. Een RSA handtekening van dit soort is deterministisch, en de ondertekenaar laat het ondertekeningstijdstip weg tenzij je erom vraagt, dus het bestand hangt alleen af van de sleutel en de invoer, niet van de machine die ondertekende. Een tijdstempel is het enige onderdeel dat van run tot run verschilt, omdat de tijdstempelautoriteit het moment stempelt waarop erom wordt gevraagd.

Waar de sleutel vandaan komt

Op Linux kan de sleutel afkomstig zijn van een PFX of PEM bestand, van een PKCS#11 token of HSM, of van een cloud signing service: Azure Trusted Signing, AWS KMS, Google Cloud KMS, HashiCorp Vault of een CSC remote signing service. De Windows certificaatarchief provider is degene die alleen op Windows blijft werken, om de voor de hand liggende reden.

Niet alleen EXE

Hetzelfde geldt voor elk code signing component in sgcSign: TsgcAuthenticodeSigner voor EXE en DLL, TsgcCatalogSigner voor .cat catalogi, TsgcMSISigner voor Windows Installer .msi en .msp bestanden, TsgcAppxSigner voor .msix en .appx packages, TsgcPowerShellSigner voor PowerShell scripts, en TsgcAuthenticodeVerifier om ze allemaal te controleren. Ze bouwen en draaien allemaal op Linux, macOS, iOS en Android.

Op de server en in de command line

Hetzelfde geldt buiten de library. Een sgcSign Server gebouwd voor Linux ondertekent en verifieert Authenticode, PowerShell, catalog, MSI en MSIX verzoeken precies zoals op Windows, en de sgcsign command line tool stuurt ze vanaf elk platform. Eén route blijft Windows only in de Delphi command line tool: een EXE of DLL lokaal hashen en alleen de hash versturen. De .NET command line tool volgt die route op elk platform.

Beschikbaarheid

Code signing op Linux, macOS, iOS en Android is beschikbaar vanaf sgcSign 2026.10 voor Delphi. Er verandert niets voor een Windows programma: dezelfde componenten, dezelfde eigenschappen en dezelfde uitvoer. Elke eigenschap staat gedocumenteerd in de sgcSign online help.

Vragen, of een pipeline die je van Windows af wilt halen? Neem contact op, en je krijgt antwoord van de mensen die de code hebben geschreven.