Assinar um programa Windows sempre significou precisar de uma máquina Windows. O signtool só funciona no Windows, então um pipeline de build que compila no Linux ainda precisa de um host Windows em algum lugar, só para colocar uma assinatura no arquivo no final.
A partir do sgcSign 2026.10 esse host é opcional. O formato Authenticode no sgcSign, que faz o hash do arquivo PE e monta a assinatura, agora é código Delphi em vez de uma chamada às APIs de assinatura do Windows. No Windows a operação de chave ainda passa pelo CNG, e em qualquer outro lugar ela roda na própria criptografia do sgcSign, de modo que os componentes que assinam um EXE no Windows compilam e rodam no Linux 64 bits, e o Windows aceita o arquivo que eles produzem como qualquer outro arquivo assinado. Este post mostra um pequeno programa de console que faz isso, como compilá-lo para Linux e como verificar o resultado.
O programa Delphi assinando um EXE no Ubuntu, e depois o mesmo arquivo verificado no Windows. Também no YouTube.
Por que assinar no Linux, afinal
- Agentes de build. Executores de CI hospedados e contêineres de build são, antes de tudo, Linux. Assinar no mesmo agente que compilou o arquivo elimina um salto até uma máquina Windows e as credenciais que esse salto exige.
- Onde a chave fica. Uma chave de assinatura de código pertence a um HSM ou a um cofre de chaves na nuvem, e ambos são acessados a partir do Linux com a mesma facilidade. Os provedores de chave PKCS#11 e de nuvem do sgcSign funcionam em todas as plataformas.
- Uma ferramenta em qualquer lugar. O mesmo programa Delphi assina na máquina Windows do desenvolvedor e no servidor de release, com as mesmas opções e a mesma saída.
O programa
Um programa de console que recebe o arquivo a assinar, o arquivo de saída, um PFX com sua senha e, opcionalmente, uma autoridade de carimbo de data/hora. É o mesmo código no Windows e no 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.
O TsgcPFXKeyProvider guarda o certificado e a chave privada. O TsgcAuthenticodeSigner faz o hash do arquivo PE da mesma forma que o Windows faz, monta a assinatura PKCS#7 e a grava na tabela de certificados do arquivo. Com um TsgcTSAClient conectado, ele também busca um carimbo de data/hora RFC 3161, para que a assinatura continue válida depois que o certificado expirar.
Compile para Linux 64 bits
Na janela Projects, adicione Linux 64-bit como plataforma de destino, com um SDK Linux configurado no SDK Manager, e então compile. Os pacotes de runtime do sgcSign são compilados para Linux64 a partir do Delphi 10.3. No Linux o PFX é interpretado em Pascal puro, já que não existe um repositório de certificados do Windows para importá-lo, e a operação RSA ou ECDSA roda na própria criptografia do sgcSign. O formato de assinatura ao redor dela é montado pelo mesmo código que roda no Windows.
Execute e verifique o resultado
$ 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
Para verificar sem o Windows, o osslsigncode verify recalcula o digest do arquivo e verifica a assinatura sobre ele: Signature verification: ok.
Depois copie o arquivo para o Windows. O PowerShell e o signtool verify /pa /v leem a mesma assinatura: o hash do arquivo que o Windows calcula corresponde ao que foi assinado, o assinante é o certificado de demonstração, e o carimbo de data/hora da DigiCert é verificado. O arquivo continua executando.
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
A única reclamação que o Windows faz é sobre o certificado de demonstração, não sobre a assinatura. A cadeia de demonstração termina em uma raiz de teste autoassinada que não está instalada na máquina, então o Windows não consegue vinculá-la a uma raiz confiável. O digest, a assinatura e o carimbo de data/hora são todos verificados.
Os mesmos bytes no Linux e no Windows
Assine o mesmo Hello.exe com o mesmo PFX e sem carimbo de data/hora, uma vez com a compilação Linux e outra com a compilação Windows do programa acima, e compare os dois arquivos:
Linux 82344 bytes SHA-256 6FD63BB01A8E911AB099431BC76F3BFA4EF557082BFB8BC18CCBFA97A9E8079D
Windows 82344 bytes SHA-256 6FD63BB01A8E911AB099431BC76F3BFA4EF557082BFB8BC18CCBFA97A9E8079D
Eles são idênticos, byte a byte. Uma assinatura RSA desse tipo é determinística, e o assinante deixa a hora de assinatura de fora a menos que você a solicite, de modo que o arquivo depende apenas da chave e da entrada, não da máquina que assinou. O carimbo de data/hora é a única parte que muda de execução para execução, porque a autoridade de carimbo registra o momento em que é solicitada.
De onde vem a chave
No Linux a chave pode vir de um arquivo PFX ou PEM, de um token PKCS#11 ou HSM, ou de um serviço de assinatura na nuvem: Azure Trusted Signing, AWS KMS, Google Cloud KMS, HashiCorp Vault ou um serviço de assinatura remota CSC. O provedor do repositório de certificados do Windows é o único que continua exclusivo do Windows, pelo motivo óbvio.
Não só EXE
O mesmo vale para todos os componentes de assinatura de código do sgcSign: TsgcAuthenticodeSigner para EXE e DLL, TsgcCatalogSigner para catálogos .cat, TsgcMSISigner para arquivos .msi e .msp do Windows Installer, TsgcAppxSigner para pacotes .msix e .appx, TsgcPowerShellSigner para scripts do PowerShell, e TsgcAuthenticodeVerifier para verificar qualquer um deles. Todos eles compilam e rodam em Linux, macOS, iOS e Android.
No servidor e na linha de comando
O mesmo vale além da biblioteca. Um sgcSign Server compilado para Linux assina e verifica solicitações de Authenticode, PowerShell, catálogo, MSI e MSIX exatamente como faz no Windows, e a ferramenta de linha de comando sgcsign as envia a partir de qualquer plataforma. Uma rota continua exclusiva do Windows na ferramenta de linha de comando Delphi: fazer o hash de um EXE ou de uma DLL localmente e enviar apenas o hash. A ferramenta de linha de comando .NET segue essa rota em qualquer plataforma.
Disponibilidade
A assinatura de código no Linux, macOS, iOS e Android chega no sgcSign 2026.10 para Delphi. Nada muda para um programa Windows: os mesmos componentes, as mesmas propriedades e a mesma saída. Cada propriedade está documentada na ajuda on-line do sgcSign.
Perguntas, ou um pipeline que você quer tirar do Windows? Fale conosco, e você receberá uma resposta de quem escreveu o código.
