Bir imzalama sunucusu özel anahtarı tek bir yerde tutar ve böyle bir sunucuya sahip olmanın amacı da budur. Bunun genellikle bedeli dosyanın kendisidir: derleme aracısı kurulum dosyasını yükler, sunucu onu imzalar ve tamamını geri gönderir. Birkaç gigabaytlık bir kurulum dosyası için bu, sadece birkaç kilobaytlık bir imza eklemek uğruna dosyanın ağ üzerinden iki kez geçmesi anlamına gelir.
Bunun böyle olması gerekmez. Authenticode'un imzaladığı şey hiçbir zaman dosyanın kendisi değildir, dosyanın bir özetidir ve bu özet dosyanın zaten bulunduğu yerde hesaplanabilir. sgcSign 2026.10 sürümünden itibaren sgcSign Server, daha önce bir EXE için yaptığı gibi, bir Windows Installer paketini ve bir MSIX veya APPX paketini yalnızca bu özetten imzalar.
sgcSign Server üzerinden iki kez imzalanan 1 GB'lık bir kurulum dosyası, bir kez yükleme yoluyla, bir kez de özeti üzerinden; hat üzerindeki her bayt sayılarak. YouTube'da da.
Aslında imzalanan şey
Bir Authenticode imzası bir özeti kapsar ve her format kendi özetini tanımlar. Bir EXE için bu, sağlama toplamı ve sertifika tablosu hariç tutulan PE görüntü özetidir. Bir MSI için bu, birleşik dosyanın akışları üzerinde sabit bir sırayla hesaplanan bir özettir. Bir MSIX için bu, paketin parçaları üzerindeki SHA-256 özetlerinden oluşan küçük bir bloktur. Sunucunun yalnızca bu değere ihtiyacı vardır, başka hiçbir şeye: elindeki anahtarla özeti imzalar ve istemci imzayı dosyaya yazar.
Komut satırından
Komut satırı aracı, daha önce --format authenticode için yaptığı gibi --format msi ve --format appx için de varsayılan olarak özet rotasını kullanır; --upload ise bunun yerine dosyanın tamamını gönderir. Demo, aynı makinedeki bir sunucuya karşı, aracın gönderdiği ve aldığı her baytı sayan küçük bir aktarıcı (relay) üzerinden aynı 1 GB'lık paketi iki yöntemle de imzalar. Paket 1 GiB rastgele veri içerir, böylece yol boyunca hiçbir şekilde sıkıştırılamaz.
> sgcsign sign --format msi --upload --server http://127.0.0.1:18481 --apikey $k `
--provider demo --tsa http://timestamp.digicert.com --verbose `
--out BigApp-signed-upload.msi BigApp.msi
Signed: BigApp-signed-upload.msi | Signer: O=eSeGeCe Demo, CN=sgcSign Demo Code Signing | Duration: 3015 ms
> Get-Content .\relay.log -Tail 1
RUN 4: connections=1 bytes_up(client->server)=1075356521 bytes_down(server->client)=1075364108 wall=14.087s
> sgcsign sign --format msi --server http://127.0.0.1:18481 --apikey $k `
--provider demo --tsa http://timestamp.digicert.com --verbose `
--out BigApp-signed-hash.msi BigApp.msi
[prehash] alg=sha256 hash=9151f2ee34b18f80a20d4b2cec515ac344066bbed846c7efbc98d7c5e5a0cddd
> Get-Content .\relay.log -Tail 1
RUN 5: connections=1 bytes_up(client->server)=477 bytes_down(server->client)=11044 wall=0.178s
Her iki dosya da imzalanmış olarak geri döner ve aynı özeti taşır: signtool verify /v her ikisinde de Hash of file (sha256): 9151F2EE…5A0CDDD değerini gösterir; bu, [prehash] satırındaki değerle aynıdır ve demo imzalayıcı ile DigiCert zaman damgasıyla birlikte gelir.
Rakamlar
| Yükleme rotası | Özet rotası | |
|---|---|---|
| Paket | 1.075.355.648 bayt | 1.075.355.648 bayt |
| Sunucuya gönderilen | 1.075.356.521 bayt | 477 bayt |
| Sunucudan alınan | 1.075.364.108 bayt | 11.044 bayt |
| Hat üzerindeki süre | 14,1 s | 0,18 s |
| Tüm komut | 16,3 s | 4,8 s |
Bu test loopback üzerinden çalıştırıldı; burada bayt taşımanın neredeyse hiçbir maliyeti yoktur, buna rağmen yükleme rotası bunun için hâlâ 14 saniye harcadı. Gerçek bir bağlantıda maliyetin tamamı aktarımdır: 100 Mbit/s hızında yükleme rotasının 2,15 GB'ı neredeyse üç dakika sürerken, özet rotası hâlâ yalnızca 477 bayt gönderir. Özet rotasının 4,8 saniyesinden geriye kalan, derleme aracısında 1 GB'lık paketi hazırlamak ve özetlemek için harcanan süredir; bu, yükleme rotasının da yaptığı ama sunucu üzerinde yaptığı bir iştir.
Delphi'de aynı şey
Bir Delphi derleme aracında akış dört adımdır: paketi hazırlamak, hazırlanan paketi özetlemek, özeti göndermek ve geri gelen imzayı gömmek. HTTP çağrısı, projenizin zaten kullandığı herhangi bir istemci olabilir. Bu örnekte RTL'nin THTTPClient ve System.JSON sınıfları kullanılmıştır.
program HashSignMSI;
{$APPTYPE CONSOLE}
uses
System.SysUtils, System.Classes, System.JSON, System.Net.HttpClient,
sgcSign_MSI, sgcSign_Base64, sgcSign_Authenticode;
const
CS_SERVER = 'http://127.0.0.1:18480';
var
vPrepared, vSigned, vHex, vBody: string;
vHash, vPKCS7: TBytes;
vI: Integer;
oHTTP: THTTPClient;
oFields: TStringList;
oResponse: IHTTPResponse;
oJSON: TJSONValue;
begin
try
vPrepared := ChangeFileExt(ParamStr(1), '.prepared.msi');
vSigned := ChangeFileExt(ParamStr(1), '.signed.msi');
// 1. Prepare: this is the package the digest covers
sgcMSIPrepareForSigning(ParamStr(1), vPrepared);
// 2. Hash the PREPARED package on this machine
vHash := sgcMSIComputeHash(vPrepared, ahSHA256);
vHex := '';
for vI := 0 to Length(vHash) - 1 do
vHex := vHex + LowerCase(IntToHex(vHash[vI], 2));
Writeln('SHA-256 : ', vHex);
// 3. Only the digest leaves the machine
oHTTP := THTTPClient.Create;
oFields := TStringList.Create;
try
oHTTP.CustomHeaders['X-API-Key'] := GetEnvironmentVariable('SGCSIGN_APIKEY');
oFields.Add('hash=' + vHex);
oFields.Add('alg=sha256');
oFields.Add('provider=demo');
oFields.Add('tsa_url=http://timestamp.digicert.com');
oFields.Add('level=t');
oResponse := oHTTP.Post(CS_SERVER + '/api/v1/sign/msi/hash', oFields);
vBody := oResponse.ContentAsString(TEncoding.UTF8);
if oResponse.StatusCode <> 200 then
raise Exception.CreateFmt('HTTP %d: %s', [oResponse.StatusCode, vBody]);
finally
oFields.Free;
oHTTP.Free;
end;
// 4. Base64-decode the PKCS#7 and embed it into the SAME prepared package
oJSON := TJSONObject.ParseJSONValue(vBody);
try
vPKCS7 := TsgcBase64.Decode(oJSON.GetValue<string>('signature'));
finally
oJSON.Free;
end;
sgcMSIEmbedSignature(vPrepared, vSigned, vPKCS7);
Writeln('PKCS#7 : ', Length(vPKCS7), ' bytes');
Writeln('Signed : ', vSigned);
except
on E: Exception do
begin
Writeln(ErrOutput, 'error: ', E.Message);
ExitCode := 1;
end;
end;
end.
Aynı sunucudan 7.941 bayt boyutunda bir PKCS#7 geri döndü ve signtool sonucu yukarıdaki iki dosyada olduğu gibi okur.
Önce hazırlayın, sonra özetleyin
Bir kurulum paketi, teslim edileceği son haliyle imzalanır, bu yüzden özetlenmeden önce o hale getirilmesi gerekir. sgcMSIPrepareForSigning ve sgcAppxPrepareForSigning bunu yapar ve kural basittir: prepare'ın size verdiği şeyi özetleyin ve imzayı orijinaline değil, aynı hazırlanmış pakete gömün. Bunun yerine orijinali özetlerseniz, imza artık var olmayan baytları kapsar.
MSIX: yayımcıyı kendiniz kontrol edin
Özet rotasında hiçbir taraf, Windows'un titizlikle üzerinde durduğu tek kontrolün her iki yarısını birden görmez. Sunucu sertifikaya sahiptir ama manifestoyu asla görmez, istemci ise manifestoya sahiptir ama sertifikayı asla görmez. Bu yüzden her özet yanıtı, imzalayan sertifikanın konusu olan signer_subject alanını taşır. İmzayı gömmeden önce bunu sgcAppxReadPublisher ve sgcAppxDNMatches ile paketin Publisher değeriyle karşılaştırın. Komut satırı aracı bunu kendiliğinden yapar ve değerler farklıysa paketi yazmayı reddeder.
Yine de ne zaman yüklenmeli
Tam dosya rotaları hâlâ mevcuttur. İstemci özeti kendisi hesaplayamadığında veya onay iş akışı yalnızca tam dosya yüklemeleriyle çalıştığı için bir imza onay gerektirdiğinde bunları kullanın. --upload, komut satırı aracını tek bir çalıştırma için o rotaya geri döndürür.
Kullanılabilirlik
Kurulum paketleri için özet rotaları olan /api/v1/sign/msi/hash ve /api/v1/sign/appx/hash, komut satırı aracının özet öncelikli varsayılanı ve prepare, hash ile embed işlevleri, Delphi ve C++Builder için sgcSign 2026.10 sürümünde sunulur. Bu rotalar sgcSign çevrimiçi yardımında belgelenmiştir.
Bir derleme çiftliğinden büyük paketler mi imzalıyorsunuz? Bizimle iletişime geçin, kodu yazan kişilerden yanıt alacaksınız.
