Windows 프로그램에 서명하려면 지금까지는 항상 Windows 머신이 필요했습니다. signtool은 Windows에서만 실행되므로, Linux에서 컴파일하는 빌드 파이프라인이라도 마지막에 파일에 서명을 넣기 위해서는 어딘가에 Windows 호스트가 필요했습니다.
sgcSign 2026.10부터는 이 호스트가 필요하지 않습니다. sgcSign의 Authenticode 형식, 즉 PE 파일을 해싱하고 서명을 구성하는 부분은 Windows 서명 API를 호출하는 대신 Delphi 코드로 구현되어 있습니다. Windows에서는 키 연산이 여전히 CNG를 거치지만, 그 외 모든 플랫폼에서는 sgcSign 자체 암호화 코드로 실행되므로, Windows에서 EXE에 서명하는 컴포넌트가 Linux 64비트에서도 빌드되고 실행되며, Windows는 그렇게 만들어진 파일을 다른 서명된 파일과 동일하게 받아들입니다. 이 글에서는 이를 수행하는 짧은 콘솔 프로그램과 Linux용 빌드 방법, 결과를 확인하는 방법을 보여줍니다.
Ubuntu에서 EXE에 서명하는 Delphi 프로그램과, 그 파일을 Windows에서 확인하는 모습입니다. YouTube에서도 볼 수 있습니다.
왜 Linux에서 서명하는가
- 빌드 에이전트. 호스팅형 CI 러너와 빌드 컨테이너는 대부분 Linux를 우선합니다. 파일을 빌드한 것과 같은 에이전트에서 서명하면 Windows 머신으로 이동하는 단계와 그 단계에 필요한 자격 증명이 사라집니다.
- 키가 있는 위치. 코드 서명 키는 HSM이나 클라우드 키 볼트에 두는 것이 맞으며, 이런 곳은 Linux에서도 똑같이 쉽게 접근할 수 있습니다. sgcSign의 PKCS#11 및 클라우드 키 제공자는 모든 플랫폼에서 동작합니다.
- 어디서나 같은 도구. 동일한 Delphi 프로그램이 개발자의 Windows 머신과 릴리스 서버에서 동일한 옵션과 동일한 결과로 서명을 수행합니다.
프로그램
서명할 파일, 출력 파일, 비밀번호가 있는 PFX, 그리고 선택적으로 타임스탬프 기관을 인자로 받는 콘솔 프로그램입니다. Windows와 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는 인증서와 개인 키를 보관합니다. TsgcAuthenticodeSigner는 Windows와 같은 방식으로 PE 파일을 해싱하고, PKCS#7 서명을 구성하여 파일의 인증서 테이블에 기록합니다. TsgcTSAClient를 연결하면 RFC 3161 타임스탬프도 함께 가져오므로, 인증서가 만료된 후에도 서명이 유효하게 유지됩니다.
Linux 64비트용으로 빌드하기
Projects 창에서 SDK Manager에 Linux SDK를 설정한 뒤 대상 플랫폼으로 Linux 64-bit를 추가하고 빌드합니다. sgcSign 런타임 패키지는 Delphi 10.3부터 Linux64용으로 빌드됩니다. Linux에서는 가져올 Windows 인증서 저장소가 없으므로 PFX를 순수 Pascal 코드로 파싱하며, RSA 또는 ECDSA 연산은 sgcSign 자체 암호화 코드로 실행됩니다. 그 주변의 서명 형식은 Windows에서 실행되는 것과 동일한 코드로 구성됩니다.
실행하고 결과 확인하기
$ 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
Windows 없이 확인하려면 osslsigncode verify가 파일의 다이제스트를 다시 계산하여 서명을 검사합니다: Signature verification: ok.
그런 다음 파일을 Windows로 복사합니다. PowerShell과 signtool verify /pa /v는 동일한 서명을 읽어 들입니다. Windows가 계산한 파일 해시는 서명 당시의 해시와 일치하고, 서명자는 데모 인증서이며, DigiCert 타임스탬프도 검증됩니다. 파일은 여전히 정상적으로 실행됩니다.
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
Windows가 유일하게 문제 삼는 부분은 서명이 아니라 데모 인증서입니다. 데모 체인은 이 머신에 설치되어 있지 않은 자체 서명 테스트 루트로 끝나기 때문에, Windows는 이를 신뢰할 수 있는 루트와 연결하지 못합니다. 다이제스트, 서명, 타임스탬프는 모두 정상적으로 검증됩니다.
Linux와 Windows에서 동일한 바이트
동일한 Hello.exe를 동일한 PFX로, 타임스탬프 없이 위 프로그램의 Linux 빌드와 Windows 빌드로 각각 한 번씩 서명한 뒤 두 파일을 비교해 보겠습니다.
Linux 82344 bytes SHA-256 6FD63BB01A8E911AB099431BC76F3BFA4EF557082BFB8BC18CCBFA97A9E8079D
Windows 82344 bytes SHA-256 6FD63BB01A8E911AB099431BC76F3BFA4EF557082BFB8BC18CCBFA97A9E8079D
두 파일은 바이트 단위까지 완전히 동일합니다. 이런 방식의 RSA 서명은 결정적(deterministic)이며, 요청하지 않는 한 서명자는 서명 시각을 포함시키지 않으므로, 파일은 서명한 머신이 아니라 오직 키와 입력에만 의존합니다. 타임스탬프는 실행할 때마다 달라지는 유일한 부분인데, 타임스탬프 기관이 요청받은 순간을 찍기 때문입니다.
키의 출처
Linux에서는 PFX나 PEM 파일, PKCS#11 토큰이나 HSM, 또는 Azure Trusted Signing, AWS KMS, Google Cloud KMS, HashiCorp Vault, CSC 원격 서명 서비스와 같은 클라우드 서명 서비스에서 키를 가져올 수 있습니다. Windows 인증서 저장소 제공자만은 당연한 이유로 Windows 전용으로 남아 있습니다.
EXE뿐만이 아닙니다
이는 sgcSign의 모든 코드 서명 컴포넌트에 동일하게 적용됩니다. EXE와 DLL을 위한 TsgcAuthenticodeSigner, .cat 카탈로그를 위한 TsgcCatalogSigner, Windows Installer .msi 및 .msp 파일을 위한 TsgcMSISigner, .msix 및 .appx 패키지를 위한 TsgcAppxSigner, PowerShell 스크립트를 위한 TsgcPowerShellSigner, 그리고 이들을 검사하는 TsgcAuthenticodeVerifier까지 모두 Linux, macOS, iOS, Android에서 빌드되고 실행됩니다.
서버와 명령줄에서도
같은 내용이 라이브러리를 넘어서도 적용됩니다. Linux용으로 빌드된 sgcSign Server는 Windows에서와 정확히 동일하게 Authenticode, PowerShell, 카탈로그, MSI, MSIX 요청에 서명하고 검증하며, sgcsign 명령줄 도구는 어떤 플랫폼에서든 이를 전송합니다. Delphi 명령줄 도구에서는 EXE나 DLL을 로컬에서 해싱한 뒤 해시만 전송하는 경로가 여전히 Windows 전용입니다. .NET 명령줄 도구는 모든 플랫폼에서 이 경로를 사용합니다.
제공 현황
Linux, macOS, iOS, Android에서의 코드 서명 기능은 Delphi용 sgcSign 2026.10에 포함되어 제공됩니다. Windows 프로그램에서는 달라지는 것이 없습니다. 동일한 컴포넌트, 동일한 속성, 동일한 결과입니다. 모든 속성은 sgcSign 온라인 도움말에 문서화되어 있습니다.
질문이 있으신가요? 아니면 Windows에서 벗어나고 싶은 파이프라인이 있으신가요? 문의하기를 이용해 주시면, 실제로 코드를 작성한 사람에게서 답변을 받으실 수 있습니다.
