Podpisanie programu dla Windows zawsze oznaczało konieczność posiadania maszyny z Windows. signtool działa wyłącznie na Windows, więc potok budowania kompilujący na Linuksie nadal potrzebował gdzieś hosta z Windows, tylko po to, by na końcu nałożyć podpis na plik.
Od sgcSign 2026.10 taki host jest opcjonalny. Format Authenticode w sgcSign, czyli haszowanie pliku PE i budowanie podpisu, to teraz kod Delphi, a nie wywołanie API podpisującego Windows. Na Windows operacja na kluczu nadal przechodzi przez CNG, a wszędzie indziej działa we własnej kryptografii sgcSign, dzięki czemu komponenty podpisujące plik EXE na Windows budują się i działają również na Linuksie 64 bitowym, a Windows akceptuje wytworzony przez nie plik jak każdy inny podpisany plik. Ten artykuł pokazuje krótki program konsolowy, który to robi, sposób zbudowania go dla Linuksa oraz sposób sprawdzenia wyniku.
Program Delphi podpisujący plik EXE na Ubuntu, a następnie ten sam plik sprawdzany na Windows. Także na YouTube.
Dlaczego w ogóle podpisywać na Linuksie
- Agenci budujący. Hostowane runnery CI i kontenery budujące działają przede wszystkim na Linuksie. Podpisywanie na tym samym agencie, który zbudował plik, eliminuje przeskok do maszyny z Windows i poświadczenia potrzebne do tego przeskoku.
- Gdzie znajduje się klucz. Klucz do podpisywania kodu powinien znajdować się w HSM lub w chmurowym magazynie kluczy, a do takich miejsc równie łatwo dotrzeć z Linuksa. Dostawcy PKCS#11 i chmurowi w sgcSign działają na każdej platformie.
- Jedno narzędzie wszędzie. Ten sam program Delphi podpisuje zarówno na maszynie z Windows programisty, jak i na serwerze wydawniczym, z tymi samymi opcjami i tym samym wynikiem.
Program
Program konsolowy przyjmujący plik do podpisania, plik wynikowy, PFX z hasłem oraz opcjonalnie urząd znacznika czasu. To ten sam kod na Windows i na Linuksie.
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 przechowuje certyfikat i klucz prywatny. TsgcAuthenticodeSigner haszuje plik PE tak, jak robi to Windows, buduje podpis PKCS#7 i zapisuje go w tabeli certyfikatów pliku. Po podłączeniu TsgcTSAClient pobiera także znacznik czasu RFC 3161, dzięki czemu podpis pozostaje ważny po wygaśnięciu certyfikatu.
Budowanie dla Linuksa 64 bitowego
W oknie Projects dodaj Linux 64-bit jako platformę docelową, ze skonfigurowanym SDK dla Linuksa w SDK Manager, a następnie zbuduj projekt. Pakiety wykonawcze sgcSign są budowane dla Linux64 od Delphi 10.3 wzwyż. Na Linuksie PFX jest parsowany w czystym Pascalu, ponieważ nie ma magazynu certyfikatów Windows, do którego można by go zaimportować, a operacja RSA lub ECDSA działa we własnej kryptografii sgcSign. Format podpisu wokół niej jest budowany przez ten sam kod, który działa na Windows.
Uruchomienie i sprawdzenie wyniku
$ 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
Aby sprawdzić to bez Windows, osslsigncode verify ponownie oblicza skrót pliku i weryfikuje podpis nad nim: Signature verification: ok.
Następnie skopiuj plik na Windows. PowerShell i signtool verify /pa /v odczytują ten sam podpis: skrót pliku obliczony przez Windows zgadza się ze skrótem, który został podpisany, podpisującym jest certyfikat demonstracyjny, a znacznik czasu DigiCert weryfikuje się poprawnie. Plik nadal się uruchamia.
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
Jedyne, do czego Windows ma zastrzeżenia, to certyfikat demonstracyjny, a nie sam podpis. Łańcuch demonstracyjny kończy się na samopodpisanym korzeniu testowym, który nie jest zainstalowany na maszynie, więc Windows nie może powiązać go z korzeniem, któremu ufa. Skrót, podpis i znacznik czasu weryfikują się poprawnie.
Te same bajty na Linuksie i na Windows
Podpisz ten sam plik Hello.exe tym samym PFX i bez znacznika czasu, raz wersją zbudowaną na Linuksie i raz wersją zbudowaną na Windows powyższego programu, a następnie porównaj oba pliki:
Linux 82344 bytes SHA-256 6FD63BB01A8E911AB099431BC76F3BFA4EF557082BFB8BC18CCBFA97A9E8079D
Windows 82344 bytes SHA-256 6FD63BB01A8E911AB099431BC76F3BFA4EF557082BFB8BC18CCBFA97A9E8079D
Są identyczne co do bajtu. Podpis RSA tego rodzaju jest deterministyczny, a podpisujący pomija czas podpisania, chyba że się o niego poprosi, więc plik zależy wyłącznie od klucza i danych wejściowych, a nie od maszyny, która go podpisała. Znacznik czasu to jedyny element, który różni się między uruchomieniami, ponieważ urząd znacznika czasu odciska moment, w którym o niego poproszono.
Skąd pochodzi klucz
Na Linuksie klucz może pochodzić z pliku PFX lub PEM, z tokena PKCS#11 lub HSM, albo z chmurowej usługi podpisującej: Azure Trusted Signing, AWS KMS, Google Cloud KMS, HashiCorp Vault lub zdalnej usługi podpisującej CSC. Dostawca magazynu certyfikatów Windows to jedyny, który pozostaje wyłącznie dla Windows, z oczywistego powodu.
Nie tylko EXE
To samo dotyczy każdego komponentu do podpisywania kodu w sgcSign: TsgcAuthenticodeSigner dla EXE i DLL, TsgcCatalogSigner dla katalogów .cat, TsgcMSISigner dla plików Windows Installer .msi i .msp, TsgcAppxSigner dla pakietów .msix i .appx, TsgcPowerShellSigner dla skryptów PowerShell oraz TsgcAuthenticodeVerifier do sprawdzania każdego z nich. Wszystkie budują się i działają na Linuksie, macOS, iOS i Androidzie.
Na serwerze i w wierszu poleceń
To samo dotyczy również poza biblioteką. sgcSign Server zbudowany dla Linuksa podpisuje i weryfikuje żądania Authenticode, PowerShell, katalogów, MSI i MSIX dokładnie tak samo jak w Windows, a narzędzie wiersza poleceń sgcsign wysyła je z dowolnej platformy. Jedna ścieżka pozostaje zarezerwowana dla Windows w narzędziu wiersza poleceń Delphi: haszowanie pliku EXE lub DLL lokalnie i wysłanie tylko skrótu. Narzędzie wiersza poleceń .NET korzysta z tej ścieżki na każdej platformie.
Dostępność
Podpisywanie kodu na Linuksie, macOS, iOS i Androidzie jest dostarczane w sgcSign 2026.10 dla Delphi. Dla programu na Windows nic się nie zmienia: te same komponenty, te same właściwości i ten sam wynik. Każda właściwość jest udokumentowana w pomocy online sgcSign.
Masz pytania albo potok, który chcesz przenieść z dala od Windows? Skontaktuj się z nami, a otrzymasz odpowiedź od osób, które napisały ten kod.
