Firmare un programma Windows ha sempre significato avere bisogno di una macchina Windows. signtool funziona solo su Windows, quindi una pipeline di build che compila su Linux ha comunque bisogno di un host Windows da qualche parte, solo per apporre una firma sul file alla fine.
A partire da sgcSign 2026.10 quell'host è opzionale. Il formato Authenticode in sgcSign, che calcola l'hash del file PE e costruisce la firma, è ora codice Delphi invece di una chiamata alle API di firma di Windows. Su Windows l'operazione di chiave passa ancora per CNG, e ovunque altrove viene eseguita nella crittografia propria di sgcSign, quindi i componenti che firmano un EXE su Windows si compilano ed eseguono anche su Linux a 64 bit, e Windows accetta il file che producono come qualsiasi altro file firmato. Questo articolo mostra un breve programma a riga di comando che lo fa, come compilarlo per Linux e come verificare il risultato.
Il programma Delphi che firma un EXE su Ubuntu, e poi lo stesso file verificato su Windows. Anche su YouTube.
Perché firmare su Linux
- Agenti di build. I runner CI ospitati e i container di build sono, prima di tutto, Linux. Firmare sullo stesso agente che ha compilato il file elimina un salto verso una macchina Windows e le credenziali che quel salto richiede.
- Dove vive la chiave. Una chiave di firma del codice appartiene a un HSM o a un cofano chiavi nel cloud, e da Linux si raggiungono entrambi con la stessa facilità. I provider di chiavi PKCS#11 e cloud di sgcSign funzionano su ogni piattaforma.
- Uno strumento ovunque. Lo stesso programma Delphi firma sulla macchina Windows di uno sviluppatore e sul server di rilascio, con le stesse opzioni e lo stesso output.
Il programma
Un programma a riga di comando che riceve il file da firmare, il file di output, un PFX con la sua password e, facoltativamente, un'autorità di marcatura temporale. È lo stesso codice su Windows e su 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 conserva il certificato e la chiave privata. TsgcAuthenticodeSigner calcola l'hash del file PE nello stesso modo di Windows, costruisce la firma PKCS#7 e la scrive nella tabella dei certificati del file. Con un TsgcTSAClient collegato, recupera anche una marca temporale RFC 3161, così la firma resta valida anche dopo la scadenza del certificato.
Compilarlo per Linux a 64 bit
Nella finestra Projects, aggiungi Linux 64-bit come piattaforma di destinazione, con un SDK Linux configurato nell'SDK Manager, quindi compila. I pacchetti runtime di sgcSign sono compilati per Linux64 a partire da Delphi 10.3. Su Linux il PFX viene interpretato in Pascal puro, dato che non esiste un archivio certificati di Windows in cui importarlo, e l'operazione RSA o ECDSA viene eseguita nella crittografia propria di sgcSign. Il formato di firma che lo circonda viene costruito dallo stesso codice che gira su Windows.
Eseguirlo e verificare il risultato
$ 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
Per verificarlo senza Windows, osslsigncode verify ricalcola il digest del file e verifica la firma su di esso: Signature verification: ok.
Poi copia il file su Windows. PowerShell e signtool verify /pa /v leggono la stessa firma: l'hash del file calcolato da Windows corrisponde a quello firmato, il firmatario è il certificato demo, e la marca temporale DigiCert viene verificata. Il file continua a funzionare.
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
L'unico rilievo che fa Windows riguarda il certificato demo, non la firma. La catena demo termina in una radice di test autofirmata che non è installata sulla macchina, quindi Windows non può collegarla a una radice attendibile. Il digest, la firma e la marca temporale vengono tutti verificati.
Gli stessi byte su Linux e su Windows
Firma lo stesso Hello.exe con lo stesso PFX e senza marca temporale, una volta con la build Linux e una volta con la build Windows del programma sopra, e confronta i due file:
Linux 82344 bytes SHA-256 6FD63BB01A8E911AB099431BC76F3BFA4EF557082BFB8BC18CCBFA97A9E8079D
Windows 82344 bytes SHA-256 6FD63BB01A8E911AB099431BC76F3BFA4EF557082BFB8BC18CCBFA97A9E8079D
Sono identici, byte per byte. Una firma RSA di questo tipo è deterministica, e il firmatario omette l'orario di firma a meno che non lo si richieda, quindi il file dipende solo dalla chiave e dall'input, non dalla macchina che ha firmato. La marca temporale è l'unica parte che cambia da un'esecuzione all'altra, perché l'autorità di marcatura temporale registra il momento in cui viene interpellata.
Da dove viene la chiave
Su Linux la chiave può provenire da un file PFX o PEM, da un token PKCS#11 o HSM, oppure da un servizio di firma nel cloud: Azure Trusted Signing, AWS KMS, Google Cloud KMS, HashiCorp Vault o un servizio di firma remota CSC. Il provider dell'archivio certificati di Windows è l'unico che resta esclusivo di Windows, per il motivo ovvio.
Non solo EXE
Lo stesso vale per ogni componente di firma del codice in sgcSign: TsgcAuthenticodeSigner per EXE e DLL, TsgcCatalogSigner per i cataloghi .cat, TsgcMSISigner per i file .msi e .msp di Windows Installer, TsgcAppxSigner per i pacchetti .msix e .appx, TsgcPowerShellSigner per gli script PowerShell, e TsgcAuthenticodeVerifier per verificarli tutti. Tutti si compilano ed eseguono su Linux, macOS, iOS e Android.
Sul server e nella riga di comando
Lo stesso vale oltre la libreria. Un sgcSign Server compilato per Linux firma e verifica le richieste di Authenticode, PowerShell, catalogo, MSI e MSIX esattamente come su Windows, e lo strumento a riga di comando sgcsign le invia da qualsiasi piattaforma. Una via resta riservata a Windows nello strumento a riga di comando Delphi: calcolare l'hash di un EXE o di una DLL localmente e inviare solo l'hash. Lo strumento a riga di comando .NET percorre questa via su qualsiasi piattaforma.
Disponibilità
La firma del codice su Linux, macOS, iOS e Android arriva in sgcSign 2026.10 per Delphi. Nulla cambia per un programma Windows: gli stessi componenti, le stesse proprietà e lo stesso output. Ogni proprietà è documentata nella guida online di sgcSign.
Domande, o una pipeline che vuoi spostare via da Windows? Contattaci, e riceverai una risposta dalle persone che hanno scritto il codice.
