---
title: 'Yapı Çıktısı İmzalama ve Doğrulama'
source: 'https://academia.sh/tr/kurslar/ci-cd/yapi-ciktisi-imzalama-ve-dogrulama'
course: 'Sürekli Tümleştirme ve Teslimat'
language: tr
updated: '2026-08-17T18:09:03+00:00'
license: 'CC BY-SA 4.0'
---

# Yapı Çıktısı İmzalama ve Doğrulama

Kaynak işlemesinden dağıtılan çıktıya uzanan zincir gerçek bir anahtar çiftiyle kurulur ve iki soru sayıya çevrilir: doğrulama zincirin hangi noktasında yapılırsa kaç sahte çıktı geçer, ve o anahtara erişen kaç özne imzalayabilir.

Önceki ders envanteri üretti ve bir boşlukla bitti: 107 kayıtlık dosya ile tarif ettiği çıktı
arasında hiçbir bağ yoktu. Aynı boşluk zincirin bütün halkalarında vardır. Hattın okuduğu kaynak
işlemesi ile ürettiği yapı çıktısı arasında, o çıktı ile depoya yazılan bayt arasında, depodaki
bayt ile dağıtılan şey arasında — her geçişte nesne el değiştirir ve **el değiştirdiğinin
kanıtı yoktur.**

Bu ders o bağı kurar ve iki soruyu sayıya çevirir: doğrulama zincirin hangi noktasında yapılırsa
kaç sahte çıktı geçer, ve imzayı üretebilen kaç özne vardır. M22/K02 imzalama zincirini deponun
içinde kurmuş, doğrulanmayan halkanın açtığı yolu saymıştı; o kurulum tekrarlanmaz. Buradaki eksen
**doğrulamanın yeri** ve **anahtarın kimde durduğu**dur.

**DD25.** İmzalama gerçek bir anahtar çiftiyle yapılır, ama anahtar her koşumda yeniden üretilir;
basılan şey doğrulama sonucudur, imzanın kendisi değil. Gerçek anahtar ya da sır yazılmaz.
**DD26.** Zincir dört halkadır: kaynak işlemesi, yapı çıktısı, envanter ve beyan; beyan üç özeti
bağlar, imza beyanı bağlar.

## Zincirin Dört Halkası

```js
// olcum-agi/zincir.mjs — kaynak islemesi, yapi ciktisi, envanter, beyan: zincir kurulur ve sinanir
import { generateKeyPairSync, sign, verify, createHash } from "node:crypto";

const ozet = (b) => createHash("sha256").update(b).digest();
const kisa = (b) => ozet(b).toString("hex").slice(0, 12);
// Kurgu anahtar cifti her kosumda yeniden uretilir; gercek anahtar ya da sir yazilmaz.
const { publicKey: acik, privateKey: gizli } = generateKeyPairSync("ed25519");
const { privateKey: baskaGizli } = generateKeyPairSync("ed25519");

// Halka 1: kaynak islemesi — agac icerigi ozetlenir. Halka 2: yapi ciktisi. Halka 3: envanter.
const kaynak = Buffer.from(["app.js", "ayar.json", "olcum.js"]
  .map((y) => `${y}\n// kurgu bolgesel olcum agi\n`).join(""));
const cikti = Buffer.concat([Buffer.from("govde\n"), Buffer.alloc(24576, "u")]);
const envanter = Buffer.from(                     // onceki dersin envanteri, 107 kayit
  Array.from({ length: 107 }, (_, i) => `bilesen-${i}@2.3`).join("\n"));

// Halka 4: beyan uc ozeti baglar; imza beyani baglar.
const beyanYaz = (surum, k, c, e) => Buffer.from([`servis=toplayici`, `surum=${surum}`,
  `kaynak=${kisa(k)}`, `cikti=${kisa(c)}`, `envanter=${kisa(e)}`].join("\n"));
const BEYAN = beyanYaz(41, kaynak, cikti, envanter);
const IMZA = sign(null, ozet(BEYAN), gizli);
console.log(`beyan ${BEYAN.length} bayt, imza ${IMZA.length} bayt; ` +
  `cikti ${cikti.length} bayt, envanter ${envanter.length} bayt`);

// Dogrulayici uc ayri sinama yapar: imza, cikti ozeti, envanter ozeti.
const dogrula = (b, i, ac, c, e) => ({
  imza: verify(null, ozet(b), ac, i),
  cikti: b.includes(`cikti=${kisa(c)}`),
  envanter: b.includes(`envanter=${kisa(e)}`),
});

const bozukCikti = Buffer.from(cikti); bozukCikti[9000] = 0x41;   // tek bayt
const bozukEnvanter = Buffer.from(envanter.toString().replace("bilesen-3@2.3", "bilesen-3@1.4"));
const SENARYO = [
  ["bozulmamis", BEYAN, IMZA, acik, cikti, envanter],
  ["beyanda surum degistirilmis", beyanYaz(39, kaynak, cikti, envanter), IMZA, acik, cikti, envanter],
  ["ciktinin tek bayti degismis", BEYAN, IMZA, acik, bozukCikti, envanter],
  ["envanter degistirilmis", BEYAN, IMZA, acik, cikti, bozukEnvanter],
  ["baska anahtarla imzalanmis", BEYAN, sign(null, ozet(BEYAN), baskaGizli), acik, cikti, envanter],
];

const SUTUN = [32, 8, 13, 17, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(SUTUN[i])).join(""));
console.log("");
satir(["senaryo", "imza", "cikti ozeti", "envanter ozeti", "kabul"]);
for (const [ad, b, i, ac, c, e] of SENARYO) {
  const s = dogrula(b, i, ac, c, e);
  satir([ad, s.imza, s.cikti, s.envanter, s.imza && s.cikti && s.envanter]);
}
```

```
beyan 86 bayt, imza 64 bayt; cikti 24582 bayt, envanter 1601 bayt

senaryo                         imza    cikti ozeti  envanter ozeti   kabul
bozulmamis                      true    true         true             true
beyanda surum degistirilmis     false   true         true             false
ciktinin tek bayti degismis     true    false        true             false
envanter degistirilmis          true    true         false            false
baska anahtarla imzalanmis      false   true         true             false
```

Bağın bedeli iki kalemdir: **86 baytlık beyan ve 64 baytlık imza.** Bağladıkları 24.582 baytlık
çıktı ile 1.601 baytlık envanterdir; 150 bayt, 26 kilobaytı yerine çiviler.

Tablo zincirin ayrıştığı yeri gösterir. **Üç sınamanın her biri tam bir senaryoyu yakalar ve
ötekiler o senaryoyu kaçırır.** İmza sınaması beyandaki sürüm değiştirildiğinde ve imza başka bir
anahtardan geldiğinde düşer — dört sahtelikten ikisini yakalar. Ama çıktının tek baytı
değiştiğinde imza hâlâ geçerlidir: beyan bozulmamıştır, bozulan beyanın **tarif ettiği** şeydir.
Aynı biçimde envanterdeki tek bir sürüm numarası değiştirildiğinde de imza doğrulanır.

Bu, kapının marjinal değerinin zincirin içine uygulanmış hâlidir. İmza sınaması tek başına
kullanıldığında dört sahtelikten ikisi geçer. Çıktı özeti sınaması yalnız kendi senaryosunu
yakalar, envanter özeti sınaması yalnız kendininkini. Üçü birlikte dördünü de tutar ve bunun
bedeli üç özet hesabıdır.

Bir ayrıntı üç sınamayı da belirler: **ikisi nesnenin kendisini ister.** İmza yalnız beyanla
sınanabilir, ama çıktı özeti çıktının baytlarını, envanter özeti envanterin baytlarını yeniden
okumayı gerektirir. Nesnesi elinde olmayan bir doğrulayıcı o iki sınamayı yapamaz — ve doğrulamanın
nerede yapıldığı sorusu tam olarak buradan çıkar.

## Doğrulama Nerede Yapılıyor

**DD27.** Dağıtım denemeleri kurgudur; her deneme gerçekten imzalanır, sahtelik **imzadan sonra**
uygulanır ve tohum `31071` görünürdür. **DD28.** Otuzuncu denemeden sonra anahtar döndürülür ve
servisin yetkili öznesi değişir.

```js
// olcum-agi/dagitim.mjs — dogrulama nerede yapilirsa kac sahte cikti geciyor
import { generateKeyPairSync, sign, verify, createHash } from "node:crypto";

const ozet = (b) => createHash("sha256").update(b).digest();
const kisa = (b) => ozet(b).toString("hex").slice(0, 12);
const A = generateKeyPairSync("ed25519");        // servisin ilk yetkili oznesi
const B = generateKeyPairSync("ed25519");        // guvenilen ama bu servis icin yetkisiz ozne
const C = generateKeyPairSync("ed25519");        // donduruldukten sonraki yetkili ozne
const der = (k) => k.export({ type: "spki", format: "der" }).toString("base64");
const GUVENILEN = [A, B, C].map((k) => k.publicKey);   // dogrulayicinin tanidigi anahtarlar

const uretec = (tohum) => () =>
  (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;
const r = uretec(31071);                         // tohum gorunur
const SAHTE = ["depoda cikti degistirilmis", "envanter degistirilmis",
  "baska oznenin imzasi", "eski surumun yeniden dagitimi"];

// Kurgu dagitim denemeleri: her biri gercekten imzalanir, sahtelik imzadan SONRA uygulanir.
// 30. denemeden sonra anahtar donduruldu: yetkili ozne A degil C.
const DENEME = [];
for (let i = 0, surum = 40; i < 60; i++) {
  const tur = r() < 0.6 ? "gecerli" : SAHTE[Math.floor(r() * SAHTE.length)];
  const yeni = tur === "eski surumun yeniden dagitimi" ? surum - 3 : ++surum;
  let cikti = Buffer.alloc(4096, String.fromCharCode(97 + (i % 26)));
  let env = Buffer.from(`envanter-${i}\n` + "kayit\n".repeat(107));
  const imzalayan = tur === "baska oznenin imzasi" ? B : (i < 30 ? A : C);
  const beyan = Buffer.from(`servis=toplayici\nsurum=${yeni}\n` +
    `cikti=${kisa(cikti)}\nenvanter=${kisa(env)}`);
  const imza = sign(null, ozet(beyan), imzalayan.privateKey);
  if (tur === "depoda cikti degistirilmis") cikti = Buffer.alloc(4096, "z");
  if (tur === "envanter degistirilmis") env = Buffer.concat([env, Buffer.from("ek\n")]);
  DENEME.push({ tur, surum: yeni, cikti, env, beyan, imza, acik: imzalayan.publicKey });
}

// Bes disiplin: zincir ayni, degisen sey dogrulamanin YERI ve ozne listesi.
const ESKI = [A].map((k) => der(k.publicKey)), GUNCEL = [A, C].map((k) => der(k.publicKey));
const DISIPLIN = [
  ["dogrulama yok", {}],
  ["yalniz hat sonunda", { hat: 1 }],
  ["dagitim aninda: imza ve ozetler", { imza: 1, ozet: 1 }],
  ["+ ozne listesi (guncellenmemis)", { imza: 1, ozet: 1, ozne: ESKI, taze: 1 }],
  ["+ ozne listesi (guncel)", { imza: 1, ozet: 1, ozne: GUNCEL, taze: 1 }],
];

const SAHTE_SAY = DENEME.filter((d) => d.tur !== "gecerli").length;
const GECERLI_SAY = DENEME.length - SAHTE_SAY;
const SUTUN = [35, 20, 20, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(SUTUN[i])).join(""));
satir(["disiplin", "kabul edilen sahte", "durdurulan gecerli", "gecen sahte turu"]);
for (const [ad, k] of DISIPLIN) {
  let sonSurum = 40, gecerliGecen = 0;
  const gecen = [];
  for (const d of DENEME) {
    // Hat sonu dogrulamasi: cikti uretildigi anda dogruydu; sahtelik sonra uygulandi.
    if (k.imza && !GUVENILEN.some((a) => verify(null, ozet(d.beyan), a, d.imza))) continue;
    if (k.ozet && !(d.beyan.includes(`cikti=${kisa(d.cikti)}`) &&
      d.beyan.includes(`envanter=${kisa(d.env)}`))) continue;
    if (k.ozne && !k.ozne.includes(der(d.acik))) continue;
    if (k.taze && d.surum <= sonSurum) continue;
    sonSurum = Math.max(sonSurum, d.surum);
    d.tur === "gecerli" ? gecerliGecen++ : gecen.push(d.tur);
  }
  satir([ad, `${gecen.length}/${SAHTE_SAY}`, `${GECERLI_SAY - gecerliGecen}/${GECERLI_SAY}`,
    [...new Set(gecen)].map((t) => t.split(" ")[0]).join(", ") || "-"]);
}

console.log("");
console.log(`gecerli ${GECERLI_SAY}; ` +
  SAHTE.map((t) => `${t.split(" ")[0]} ${DENEME.filter((d) => d.tur === t).length}`).join(", "));
```

```
disiplin                           kabul edilen sahte  durdurulan gecerli  gecen sahte turu
dogrulama yok                      18/18               0/42                envanter, eski, baska, depoda
yalniz hat sonunda                 18/18               0/42                envanter, eski, baska, depoda
dagitim aninda: imza ve ozetler    9/18                0/42                eski, baska
+ ozne listesi (guncellenmemis)    0/18                18/42               -
+ ozne listesi (guncel)            0/18                0/42                -

gecerli 42; depoda 4, envanter 5, baska 6, eski 3
```

İlk iki satır bu dersin en sert sayısıdır: **doğrulama yokken de, yalnız hat sonunda yapılırken de
on sekiz sahtenin on sekizi geçer.** Hat kendi ürettiği çıktıyı doğrular ve doğru bulur — çünkü o
anda çıktı gerçekten doğrudur. Sahtelik imzadan sonra uygulanır. **Yakalamak istediği olaydan önce
duran bir kapının marjinal değeri sıfırdır** ve bu, kapının zayıf olmasından değil, yanlış yerde
olmasından gelir.

Üçüncü satır doğrulamayı dağıtım anına taşır ve sayı yarıya iner: 18 sahteden 9'u geçer. Tutulan
dokuz tanesi depoda değiştirilen çıktı (4) ile değiştirilen envanterdir (5) — her ikisi de nesnenin
baytları yeniden okunduğu için yakalanır. Geçen dokuz tanesi ise **imzası geçerli olanlardır.**
Altısında imza başka bir öznenin geçerli anahtarındandır; doğrulayıcı "tanıdığım bir anahtar
imzalamış" der ve geçirir. Üçünde her şey gerçektir, yalnız eskidir: geçmiş bir sürümün beyanı ve
imzası bugün de doğrulanır.

Dördüncü satır iki kuralı ekler — imzalayan servisin yetkili öznesi olmalı, sürüm son dağıtılandan
büyük olmalı — ve sahte sayısı **sıfıra** iner. Ama aynı satırda ikinci sayı belirir: **42 geçerli
dağıtımın 18'i durdurulur.** Anahtar otuzuncu denemeden sonra döndürülmüştür ve doğrulayıcının
özne listesi eski anahtarda kalmıştır. Kapı sahtenin tamamını tutar ve geçerli dağıtımların
%43'ünü de tutar.

Bu, kapının üçüncü sayısını yazar. Doğrulamanın kendisi bir özet hesabı ve bir liste aramasıdır;
eklediği süre ölçüsüzdür. Bekleten şey doğrulama değil, **listenin tazeliğidir**: anahtar
döndükten sonra liste güncellenene kadar her geçerli dağıtım durur. Ardışık on sekiz geçerli
dağıtımı durduran bir kapının ekipte ürettiği davranış bilinir — kapı bir kez atlanır ve atlama
kalıcılaşır. Son satır aynı zinciri güncel listeyle koşar: 0 sahte, 0 yanlış durdurma.

## Anahtar Kaç Öznede

Dördüncü satır bir soruyu açıkta bırakır: "yetkili özne" tekil bir şey midir? İmzayı üretebilen,
gizli anahtara erişebilen herkestir.

**DD29.** Saklama modelleri kurgudur; anahtara erişen özne sayısı modelin girdisidir.

```js
// olcum-agi/anahtar.mjs — anahtarin kendisi bir kapidir: kac ozne imzalayabiliyor
import { generateKeyPairSync, sign, verify, createHash } from "node:crypto";

const ozet = (b) => createHash("sha256").update(b).digest();
const der = (k) => k.export({ type: "spki", format: "der" }).toString("base64");
const beyan = (i) => Buffer.from(`servis=toplayici\nsurum=${100 + i}`);

// Kurgu saklama modelleri: anahtar nerede duruyorsa, oraya erisen her ozne imzalayabilir.
const SAKLAMA = [
  ["gizli anahtar depoda", 23],
  ["hat calistiricisinda ortam degiskeni", 11],
  ["imzalama hizmeti, cagri yetkisi uc ozne", 3],
  ["donanimda, tek otomatik ozne", 1],
];

const SUTUN = [42, 8, 14, 15, 0];
const satir = (s) => console.log(s.map((v, i) => String(v).padEnd(SUTUN[i])).join(""));
satir(["saklama", "ozne", "paylasilan", "ozne basina", "ayirt edilen"]);

for (const [ad, n] of SAKLAMA) {
  // (a) Paylasilan tek anahtar: n ozne ayni gizli anahtarla imzalar.
  const tek = generateKeyPairSync("ed25519");
  const paylasilan = Array.from({ length: n }, (_, i) => sign(null, ozet(beyan(i)), tek.privateKey))
    .filter((im, i) => verify(null, ozet(beyan(i)), tek.publicKey, im)).length;

  // (b) Ozne basina anahtar: yetkili liste tek oznedir, otekiler kendi anahtarlariyla imzalar.
  const anahtarlar = Array.from({ length: n }, () => generateKeyPairSync("ed25519"));
  const LISTE = [der(anahtarlar[0].publicKey)];
  const oznece = anahtarlar
    .map((a, i) => [a, sign(null, ozet(beyan(i)), a.privateKey), i])
    .filter(([a, im, i]) => LISTE.includes(der(a.publicKey)) &&
      verify(null, ozet(beyan(i)), a.publicKey, im)).length;

  // Ayirt edilen imzalayan: dogrulayicinin n imzada gordugu farkli acik anahtar sayisi.
  const gorulen = (d) => new Set(d).size;
  satir([ad, n, `${paylasilan}/${n}`, `${oznece}/${n}`,
    `${gorulen(Array(n).fill(der(tek.publicKey)))} -> ${gorulen(anahtarlar.map((a) => der(a.publicKey)))}`]);
}
```

```
saklama                                   ozne    paylasilan    ozne basina    ayirt edilen
gizli anahtar depoda                      23      23/23         1/23           1 -> 23
hat calistiricisinda ortam degiskeni      11      11/11         1/11           1 -> 11
imzalama hizmeti, cagri yetkisi uc ozne   3       3/3           1/3            1 -> 3
donanimda, tek otomatik ozne              1       1/1           1/1            1 -> 1
```

Paylaşılan tek anahtarla, anahtara erişen **her öznenin** imzası doğrulanır: 23 özneden 23'ü,
11'den 11'i. Doğrulayıcı hiçbirini reddedemez, çünkü hepsi aynı anahtardır. Son sütun bunu
niceliğe çevirir — doğrulayıcı 23 imzada **tek bir açık anahtar** görür. İmzadan imzalayana giden
yol yoktur; "yetkili özne imzaladı" cümlesi ölçülemez bir cümledir.

Özne başına anahtar ve tek adlık bir yetkili liste, kabul sayısını **özne sayısından bağımsız
kılar**: 23'te de 11'de de 3'te de kabul 1'dir. Ayırt edilen imzalayan sayısı ise özne sayısına
eşitlenir. Aradaki fark aynı algoritmayla, aynı imza uzunluğuyla elde edilir; değişen tek şey
anahtarın kaç öznede durduğudur.

M22/K02 imzalamanın güveni ortadan kaldırmadığını, anahtara taşıdığını göstermişti. Buradaki tablo
o taşınmanın **büyüklüğünü** verir. Anahtar depoda dururken imza "depoyu okuyabilen 23 özneden biri
bunu imzaladı" der; donanımda tek bir otomatik öznede dururken "o özne imzaladı" der. İki cümle
arasındaki fark 23 katıdır ve önceki dersteki envanter kapsamı gibi, kapının gücünün üst sınırını
kapının kendisi değil, dışındaki bir sayı belirler.

## Özet

- Zincir dört halkadır ve bağın bedeli 86 baytlık beyan ile 64 baytlık imzadır; bağladıkları
  24.582 baytlık çıktı ve 1.601 baytlık envanterdir.
- Üç sınamanın her biri tam bir senaryoyu yakalar: imza sınaması dört sahtelikten ikisini tutar,
  çıktı özeti ile envanter özeti kalan ikisini ayrı ayrı tutar. İkisi nesnenin baytlarını yeniden
  okumayı gerektirir.
- Doğrulama yokken 18 sahtenin 18'i geçer; yalnız hat sonunda doğrulanırken de 18'i geçer, çünkü
  sahtelik imzadan sonra uygulanır. Yanlış yerde duran kapının marjinal değeri sıfırdır.
- Dağıtım anında imza ve özetler sınandığında 9 sahte geçer: 6'sı başka öznenin geçerli imzası,
  3'ü geçmiş sürümün yeniden dağıtımı. Özne listesi ve tazelik kuralıyla sahte sayısı 0'a iner.
- Güncellenmemiş özne listesi sahtenin tamamını tutar ama 42 geçerli dağıtımın 18'ini de durdurur;
  bekleten şey doğrulama değil, listenin tazeliğidir.
- Paylaşılan anahtarla 23 öznenin 23'ünün imzası kabul edilir ve doğrulayıcı tek bir açık anahtar
  görür; özne başına anahtarla kabul 1'e, ayırt edilen imzalayan 23'e çıkar.

## Sonraki Adım

Zincir artık kurulu, dağıtım anında sınanıyor ve anahtar tek bir öznede duruyor. Ölçülen sayılar
bir noktaya kadar sağlam: 0 sahte, 0 yanlış durdurma, 150 baytlık bağ. Ama o tek özne bir insan
değil, **hattın kendisidir** — ve bu bölümdeki bütün sayılar hattın dürüst çalıştığı varsayımı
üstünde durur.

O varsayım şimdiye kadar hiç sayılmadı. Hattın koştuğu iş akışı tanımı depodan gelir ve depoyu
değiştirebilen kaç özne vardır? Hat çalıştırıcısı imzalama anahtarına, yapı çıktısı deposuna ve
dağıtım hedefine aynı anda erişir; bir aşamanın çektiği bir bağımlılık o erişimlerin hepsini
kullanabilir mi? Sonraki ders bu kursun kapılarını **kapıları koyan makinenin üstüne** çevirir ve
aynı üç sayıyı hattın kendisi için sorar: ne tutuyor, ne kaçırıyor, ne kadar bekletiyor?
