Dokümantasyon

mm.erp README

Kütüphanenin tam mimari rehberi — kullanım örnekleri ve üretim deneyimi notları.

mm.erp — e-Defter İmzalama ve Doğrulama Kütüphanesi

GİB e-Defter (Yevmiye, Kebir) ve Berat XML dosyalarını XAdES-BES formatında imzalayan ve doğrulayan, .NET 10 üzerine bina edilmiş, Ma3Api'ye bağımlı olmayan, üretim-onaylı kütüphane.

İmzalama Ma3Api'nin ürettiği wire-format ile bit düzeyinde uyumlu çıktı üretir; hem GİB e-Defter web servisi (Ma3Api validator) hem de GİB e-Defter Görüntüleyici (Apache Santuario) tarafından "matematiksel olarak doğrulu" kabul edilir.


1. Genel Amaç

  • GİB e-Defter (Yevmiye/Kebir) ve Berat XML dosyalarını dijital olarak imzala.
  • Mevzuata uygun XAdES-BES zarfı üret (xades:SignedProperties, ds:SignedInfo, ds:Signature, ds:KeyInfo).
  • Üretilmiş imzalı dosyaları bağımsız doğrula (kontrol mekanizması — bkz. §6).
  • Dört anahtar kaynağını (USB token, HSM, Windows sertifika deposu, PFX dosyası) şeffafça destekle.

2. Katmanlar ve Sorumluluklar

┌─────────────────────────────────────────────────────────────┐
│  Çağıran Uygulama / CLI (tools/SignerCli)                   │
│   sign-pfx | sign-p11 | sign-csp | verify                   │
└─────────────────────────────────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────────────┐
│  MegaEDefter.Signing.EDefterSigner  (dosya/byte sarmalayıcı)│
│    • CRLF→LF normalize, XmlDocument load, byte[] döner      │
└─────────────────────────────────────────────────────────────┘
                          │
                          ▼
┌─────────────────────────────────────────────────────────────┐
│  MegaEDefter.Crypto.XAdES.XAdesBesSigner   (asıl motor)    │
│    • SignedProperties, Reference, SignedInfo, KeyInfo       │
│    • c14n, pretty-print, digest, sign, base64               │
└─────────────────────────────────────────────────────────────┘
                          │ ISigningKey (algoritma + cihaz soyutlaması)
                          ▼
┌────────────────────────┬────────────────────────┬─────────────────┐
│ Pkcs11SigningKey       │ WindowsCspSigningKey   │ PfxSigningKey   │
│ USB e-Mali Mühür / HSM │ Windows sertifika dep. │ Geliştirme/test │
│ akisp11.dll /          │ CryptoAPI / CNG (RDP)  │ PFX dosyası     │
│ cryptoki.dll           │ + HSM KSP              │                 │
└────────────────────────┴────────────────────────┴─────────────────┘
Katman Proje Açıklama
Kripto çekirdek MegaeDefter4Net.Crypto ISigningKey, XAdesBesSigner, c14n + digest, SigningSelfTest, CertificateFileLoader
Yüksek seviye sarmalayıcı MegaeDefter4Net.Signing EDefterSigner, PfxSigningKey
Donanım anahtarları MegaeDefter4Net.SmartCard Pkcs11SigningKey + Pkcs11KeyOptions + Pkcs11SlotSelector + Pkcs11RetryPolicy + PinResolver/PinProtector, WindowsCspSigningKey, CngKeyDiagnostics
Doğrulama MegaeDefter4Net.Validation XAdesEnvelopeValidator, DssEquivalentValidator, XmlRepairer, XsdValidator, SchematronRunner
CLI tools/SignerCli İnce komut satırı sarmalayıcı
Birim test tests/MegaeDefter4Net.Signing.Tests XAdES round-trip, repairer, log pattern, cert utility, imza self-test, PKCS#11 options, cert-file yükleyici

3. Anahtar Soyutlama — ISigningKey

src/MegaeDefter4Net.Crypto/ISigningKey.cs

public interface ISigningKey : IDisposable
{
    X509Certificate2 Certificate { get; }
    HashAlgorithmName HashAlgorithm { get; }   // SHA-256/384/512 (anahtar gücüne göre)
    string SignatureMethodUri { get; }         // XML DSig URI
    bool IsEcdsa { get; }
    byte[] SignData(byte[] data);
    byte[] SignHash(byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding? padding = null);
}

Hash seçimi otomatik: P-256 → SHA-256, P-384 → SHA-384, P-521 → SHA-512 (NIST güç eşleme). Üretim ortamı (Mali Mühür Sürüm 3, P-384) için ecdsa-sha384 URI'si emit edilir.

Üç implementasyon

Sınıf Çalıştığı yer Kullanım amacı
Pkcs11SigningKey Herhangi bir PKCS#11 kütüphanesi: akisp11.dll (USB token) veya cryptoki.dll (HSM) Üretim — özel anahtar cihazdan çıkmaz
WindowsCspSigningKey Windows sertifika deposu (CSP/CNG), HSM KSP dahil RDP/VM — PKCS#11 RDP redirect'i görmez, CSP görür
PfxSigningKey Dosya tabanlı PFX/P12 Birim test, geliştirme, offline işler

3.1. HSM: sertifika dosyada, anahtar cihazda

Kartlarda sertifika ve anahtar birlikte durur (ortak CKA_ID). HSM'lerde genelde yalnız anahtar çifti cihazdadır, sertifika dosyada tutulur. Pkcs11SigningKey her iki senaryoyu aynı kod yolundan destekler — ayrı bir HsmSigningKey yoktur, böylece GİB wire-format kararları tek yerde kalır.

using var key = new Pkcs11SigningKey(new Pkcs11KeyOptions
{
    DriverPath = @"C:\Program Files\SafeNet\LunaClient\cryptoki.dll",
    SlotLabel = "edefter-partition",      // ya da TokenSerial / SlotId
    StrictSlotSelection = true,           // birden çok partition varsa tahmin etme
    PinProvider = PinResolver.Create(     // env > DPAPI dosyası > literal > prompt
        new PinResolver.Sources(PinEnvVar: "HSM_PIN")),
    // LoginRequired = false,             // PED-auth / önceden doğrulanmış partition
    CertificateFile = "mali-muhur.cer",   // DER veya PEM (PFX REDDEDİLİR)
    KeyLabel = "edefter-signer",          // ya da KeyIdHex = "a1b2c3"
    // VerifyKeyMatchesCertificate varsayılan true
});
new EDefterSigner(key, new XAdesSignerOptions { ClaimedRole = "Supplier" })
    .SignFile("yev.xml", "yev.signed.xml");

Sürücü nasıl bulunur? CardDriverRegistry kart (AKİS, SafeNet eToken, E-Tugra, OpenSC) ve HSM (Thales Luna, Utimaco, AWS CloudHSM, SoftHSM2) kütüphanelerini Windows ve Linux yollarında arar; MMERP_PKCS11_MODULE ortam değişkeni hepsinin önüne geçer. Otomatik seçim yalnız kart sürücüleri içindir — kurulu bir HSM kütüphanesi kendiliğinden seçilmez, --driver ile açıkça verilir. Hata metinleri cihaz türüne göre değişir (HSM'de AKİS/RDP tavsiyesi basılmaz).

dotnet run --project tools/SignerCli -- p11-drivers   # kurulu sürücüler: [KART] / [HSM]

PIN nereden gelir? Komut satırındaki --pin process listesinde ve kabuk geçmişinde görünür; üretimde kullanmayın. Öncelik: --pin-env <DEĞİŞKEN> → --pin-file <dosya> (DPAPI şifreli) → --pin (uyarı basar) → interaktif giriş (echo kapalı). PIN tembel çözülür (login anında), Pkcs11KeyOptions sırrı taşımaz ve ToString() PIN'i maskeler.

# PIN'i bir kez DPAPI ile şifreli dosyaya yaz (PIN interaktif sorulur, argüman olarak alınmaz)
dotnet run --project tools/SignerCli -- protect-pin --out C:\ProgramData\mm.erp\hsm.pin
# Servis/LocalSystem altında koşacaksa: --machine (NTFS izinlerini kısıtlayın)

DPAPI dosyası makineye (ve CurrentUser kapsamında yazan hesaba) bağlıdır; başka makineye kopyalanırsa çözülmez. Düz metin PIN dosyası bilerek reddedilir.

Login gerekmeyen cihazlar: Luna PED-auth veya önceden kimlik doğrulanmış partition'da LoginRequired = false (--no-login) ile C_Login hiç çağrılmaz. Cihaz oturumu zaten doğrulanmış açtıysa (CKR_USER_ALREADY_LOGGED_IN) bu hata sayılmaz, imzalama devam eder. UserType varsayılan CKU_USER'dır (--user-type so yalnız token yönetimi içindir, imza değil).

Slot / partition seçimi — öncelik sırası: SlotLabel → TokenSerial → SlotId (gerçek CK_SLOT_ID) → tek slot → (strict değilse) ilk slot. Label eşleşmesi trim'li ve harf-duyarsızdır (PKCS#11 label'ları boşlukla doldurur). Seçicilerden yalnız biri verilebilir. StrictSlotSelection = true iken birden çok slot varken seçici verilmemişse hata alırsınız — kart yolunda varsayılan false, yani tek okuyucu senaryosu eskisi gibi çalışır. Aynı label'a sahip iki partition da hatadır; hata mesajları her zaman slot dökümünü (slotId, label, serial, üretici, model) içerir.

Anahtar seçim sırası — sabit, tahmin içermez: KeyLabel (CKA_LABEL) → KeyIdHex (CKA_ID) → (kart yolu) sertifikanın CKA_ID'si → (sertifika dosyadan + seçici yok) cihazdaki tek anahtar. Birden çok anahtar varken seçici verilmemişse hata verir (mevcut label/ID dökümüyle) — yanlış anahtarla imzalamak yerine durur.

Anahtar–sertifika doğrulaması: VerifyKeyMatchesCertificate açıkken ctor sonunda SigningSelfTest koşar (sabit probe hash'i imzalanır, sertifikanın public key'i ile doğrulanır). Eşleşmezse imzalama başlamadan hata alırsınız — yanlış sertifika/anahtar çifti GİB'den "imza degeri gecersizdir" reddi olarak günler sonra dönmez. Kart yolunda varsayılan kapalıdır (eski davranış); --no-key-verify ile de kapatılabilir.

⚠️ CertificateFile yalnız public sertifika kabul eder. PFX/P12 verilirse reddedilir: dosyadaki bir private key'in "HSM imzaladı" sanılarak kullanılması engellenmiştir.

Oturum dayanıklılığı: İmzalama serileştirilir (PKCS#11 oturumu eşzamanlı C_Sign kaldırmaz). Oturum koparsa (kart çıkarıldı, HSM istemcisi yeniden bağlandı, boşta kalan oturum düşürüldü) slot yeniden seçilir, login tekrarlanır, anahtar aynı seçicilerle yeniden çözümlenir ve imza bir kez yeniden denenir — böylece bir kopma tüm partiyi düşürmez. Yeniden bağlanmanın ardından anahtar–sertifika eşleşmesi tekrar doğrulanır: aynı etiketli farklı bir cihaz takılmışsa imzalama durur. CKR_PIN_INCORRECT gibi operasyon hataları yeniden denenmez (tekrar denemek token kilitleyebilir).

⚠️ Durum: PKCS#11/HSM yolunun tamamı kodlandı ve birim testleriyle doğrulandı, ancak gerçek bir HSM/token üzerinde uçtan uca çalıştırılmadı (bugüne dek yalnız sürücü yükleme + slot enumerasyonu gerçek akisp11.dll ile denendi). Üretimde kullanmadan önce §1.7'deki doğrulama adımlarını koşun.

Kapsam ve durum (fazlar, cihazda doğrulanmayı bekleyenler): docs/YAPILACAKLAR.md §1 · KSP yolu için PoC runbook: docs/hsm-poc-csp.md


4. Sarmalayıcı — EDefterSigner

src/MegaeDefter4Net.Signing/EDefterSigner.cs

İki kritik küçük iş yapar:

  1. SignFile(input, output) — dosya tabanlı kullanım kolaylığı.
  2. CR/LF normalize — XML 1.0 §2.11 uyarınca CR ve CRLF'leri LF'ye çevirir. Aksi halde C14N &#xD; literal olarak emit eder → GİB Görüntüleyici "Matematiksel doğrulama HATALI" verir.
using var key    = new PfxSigningKey("test.pfx", "1234");
var signer       = new EDefterSigner(key, new XAdesSignerOptions { ClaimedRole = "Supplier" });
signer.SignFile("yevmiye.xml", "yevmiye.signed.xml");

Asıl iş alttaki XAdesBesSigner.Sign(XmlDocument)'a delege edilir.


5. Motor — XAdesBesSigner

src/MegaeDefter4Net.Crypto/XAdES/XAdesBesSigner.cs

Sign(XmlDocument) 8 adımlık akış:

  1. Skeleton kur — ds:Signature içinde boş ds:Object → xades:QualifyingProperties → SignedProperties (SigningTime, SigningCertificate, ClaimedRole, DataObjectFormat).
  2. Pretty-print — hash öncesi whitespace yerleştir (sonra c14n bunu imzaya dahil eder, verify aynı bytları görür).
  3. Reference 1 digest — SignedProperties → c14n → SHA-256.
  4. Reference 2 digest — Tüm belge, enveloped-signature transform uygulandıktan sonra c14n → SHA-256.
  5. SignedInfo inşa et — iki Reference, SignatureMethod URI, CanonicalizationMethod=xml-c14n-20010315 (plain, WithComments DEĞİL — Ma3Api kuralı).
  6. SignedInfo'yu c14n'le ve imzala — ISigningKey.SignData(...). ECDSA için P1363 → DER dönüşümü — GİB Java validatörü DER bekler.
  7. SignatureValue — base64 olarak yerleştir.
  8. KeyInfo — X509Data (sertifika + SubjectName) ÖNCE, KeyValue (modulus/exponent) SONRA. Sıra önemlidir — bazı strict validatörler şikayet eder.

Üretim deneyimiyle kazanılmış wire-format kararları

Aşağıdaki kararların hepsi gerçek GİB red/kabul vakalarıyla doğrulanmış ve kaynak kodda tarih + firma referansıyla belgelenmiştir:

  • CanonicalizationMethod = plain c14n (WithComments yok). WithComments kullanılırsa Ma3Api "Belge üzerindeki imza degeri gecersizdir" verir.
  • ECDSA imza encoding = DER (P1363 değil). GİB file-level validatörü DER bekler.
  • Reference sırası: önce SignedProperties, sonra belge (Ma3Api sırası).
  • DigestMethod = SHA-256 sabit (anahtar SHA-384 olsa bile) — Ma3Api uyumu.
  • HashForEc = anahtar gücüyle eşleşmeli: P-384 anahtar → ecdsa-sha384. Sabit-SHA-256 eski davranışı bug'tı.
  • X509IssuerName boşluksuz format (CN=...,OU=...) — binary cert encoding'iyle eşleşir.
  • xmlns:ds ve xmlns:xades yerel'de tekrar bildirilir — bazı Java verify'larında inherited vs. declared farkı c14n çıkışını değiştirir.
  • Sertifika seçim ranking'i: Mali Mühür + KeyUsage=DigitalSignature → sırayla düşer. Yanlış cert (KeyAgreement) seçimi GİB "imza geçersiz" sebebi olmuştu.
  • LF-only XmlWriter output — Windows'da default CRLF, verify-time c14n hash'i bozar.

6. Doğrulama (Kontrol Mekanizması)

Repo iki katmanlı doğrulama sunar:

6.1. Hızlı kriptografik doğrulama — XAdesBesSigner.Verify

Aynı sınıfta bulunan static Verify(XmlDocument):

  • Path A — manuel yeniden inşa: c14n method ve digest URI'leri envelope'tan okuyup birebir yeniden hashleyerek doğrular (bizim imzaladığımız dosyaları bit-bit doğrulayabilir).
  • Path B — fallback olarak .NET SignedXml.CheckSignature (başka aletlerce üretilmiş imzalar için).
SignerCli verify --in yevmiye.signed.xml --mode fast

6.2. EU DSS-eşdeğer kapsamlı doğrulama — DssEquivalentValidator

src/MegaeDefter4Net.Validation/DssEquivalentValidator.cs

EU DSS (eSignature DSS / Apache Santuario) semantiğini taklit eden tam zincirli doğrulayıcı. Her imza için 7 yapı taşı ayrı ayrı raporlanır:

  • FormatChecking — XAdES-BES yapısal kontrol
  • IdentificationOfSigningCertificate — SigningCertificate referansı, cert digest
  • ValidationContextInitialization — sertifika zinciri inşası
  • X509CertificateValidation — KamuSM kök CA listesine karşı zincir doğrulama
  • CryptographicVerification — Reference digest'leri + SignedInfo imza doğrulama
  • SignatureAcceptanceValidation — ClaimedRole, SigningTime, MimeType kontrolleri
  • AlgorithmObsolescenceValidation — SHA-1 / RSA-1024 vb. eski algoritma reddi

Çıktı: DssEquivalentReport — her imzanın Indication (TOTAL_PASSED / TOTAL_FAILED / INDETERMINATE) + DigestMatchers + Timestamps.

SignerCli verify --in yevmiye.signed.xml --mode dss

6.3. Yardımcı doğrulayıcılar

Sınıf Görev
XAdesEnvelopeValidator Yapısal: doğru element sırası, zorunlu alanlar
XsdValidator GİB edefter.xsd + XAdES.xsd şema doğrulaması
SchematronRunner GİB schematron kurallarını çalıştırır (Saxon-HE üzerinden)
XmlRepairer Yaygın bozuklukları tamir eder (encoding, period bound, MimeType)
KamuSmTrustList KamuSM trusted CA listesi yöneticisi (resources/kamusm-trusted-cas.json)

7. Legacy Entegrasyon Notu

Bu repo kütüphane + CLI olarak tasarlandı. Eski monorepo'da VB.NET tarafının (ModMega_SignersInProcess, ModMega_Sign_Operations) kullandığı entegrasyon in-process çağrıdır — Process.Start("SignerCli.exe sign-p11 …") shell-out değil:

  • ~150ms latency tasarrufu (process spawn yok)
  • Native exception propagation
  • Customer paketinde SignerCli.exe + sibling DLL'ler artık gerekmiyor

Yeni bir uygulamadan bu kütüphaneyi kullanırken: Pkcs11SigningKey / WindowsCspSigningKey / PfxSigningKey'i doğrudan new ile yarat ve EDefterSigner.SignFile(...) çağır. Çift imza (Accountant önce, Supplier sonra) sırası mevzuat gereği; XAdesSignerOptions.ClaimedRole ile her imzaya Supplier veya Accountant rolü gömülür.


8. Özet Kullanım Senaryoları

Senaryo Sınıf Sertifika kaynağı
Üretim — fiziksel USB token Pkcs11SigningKey akisp11.dll üzerinden token
RDP / VM (token client'ta, host'ta yok) WindowsCspSigningKey Windows cert store (AKİS Manager'ın yansıttığı)
Geliştirme / test / CLI PfxSigningKey PFX/P12 dosyası
HSM — PKCS#11 (sertifika dosyada) Pkcs11SigningKey + Pkcs11KeyOptions .cer/.pem dosyası; anahtar cihazda (KeyLabel/KeyIdHex) — bkz. §3.1
HSM — Windows KSP/CSP kurulu WindowsCspSigningKey (--provider <KSP>) HSM partition — bkz. docs/hsm-poc-csp.md

Hızlı başlangıç

// 1) PFX ile imza (test)
using var key = new PfxSigningKey("test.pfx", "1234");
new EDefterSigner(key).SignFile("in.xml", "out.signed.xml");

// 2) USB token ile imza (üretim)
using var key = new Pkcs11SigningKey(
    driverPath: CardDriverRegistry.AutoDetect(),
    tokenSerial: null,
    pin: "123456");
new EDefterSigner(key, new XAdesSignerOptions { ClaimedRole = "Supplier" })
    .SignFile("in.xml", "out.signed.xml");

// 3) HSM ile imza — sertifika dosyadan, anahtar cihazdan (bkz. §3.1)
using var key = new Pkcs11SigningKey(new Pkcs11KeyOptions
{
    DriverPath = @"C:\Program Files\SafeNet\LunaClient\cryptoki.dll",
    Pin = "123456",
    CertificateFile = "mali-muhur.cer",
    KeyLabel = "edefter-signer",
});
new EDefterSigner(key, new XAdesSignerOptions { ClaimedRole = "Supplier" })
    .SignFile("in.xml", "out.signed.xml");

// 4) Doğrulama (hızlı)
var doc = new XmlDocument { PreserveWhitespace = true };
doc.Load("out.signed.xml");
bool ok = XAdesBesSigner.Verify(doc);

// 5) Doğrulama (EU DSS eşdeğeri)
var report = new DssEquivalentValidator().Validate(doc, "out.signed.xml");
bool dssOk = report.OverallIndication == DssIndication.TotalPassed;

CLI ile kullanım

# Build
dotnet build -c Release

# PFX ile imzala
dotnet run --project tools/SignerCli -- sign-pfx \
    --pfx test.pfx --password 1234 \
    --in samples/sample_unsigned_berat.xml \
    --out berat.signed.xml \
    --role Supplier

# USB token ile imzala
dotnet run --project tools/SignerCli -- sign-p11 \
    --in yev.xml --out yev.signed.xml --pin 123456

# HSM ile imzala (partition seçimi + şifreli PIN dosyası + anahtar cihazda)
dotnet run --project tools/SignerCli -- sign-p11 \
    --driver "C:\Program Files\SafeNet\LunaClient\cryptoki.dll" \
    --slot-label "edefter-partition" --strict-slot \
    --pin-file C:\ProgramData\mm.erp\hsm.pin \
    --cert-file mali-muhur.cer --key-label "edefter-signer" \
    --in yev.xml --out yev.signed.xml

# Windows cert store ile imzala (RDP)
dotnet run --project tools/SignerCli -- sign-csp \
    --in yev.xml --out yev.signed.xml --vkn 1234567890

# Anahtar/provider tanılama — hangi KSP/CSP anahtarı tutuyor? (HSM devreye alma)
dotnet run --project tools/SignerCli -- csp-info
dotnet run --project tools/SignerCli -- csp-info --provider "Luna" --test-sign

# Doğrula (hızlı)
dotnet run --project tools/SignerCli -- verify --in yev.signed.xml

# Doğrula (DSS eşdeğeri, tam rapor)
dotnet run --project tools/SignerCli -- verify --in yev.signed.xml --mode dss

9. SOAP / WS-Security Notu

Bu repo e-Defter dosya imzasını kapsar — yani GİB'e gönderilecek *.xml zarfları. GİB web servisi (Ma3Api SOAP) çağrıları için WS-Security imzası ayrı bir katmandır ve bu repoda bulunmaz (orijinal monorepo'daki MegaeDefter4Net.GibClient.WsSecurity ailesi). Aynı ISigningKey soyutlaması kullanılabilir; ama SOAP envelope üzerinde çalışan ayrı SoapMessageSigner gerekir. İhtiyaç olursa ileride bu repo'ya da eklenebilir.


10. Web Sitesi İletişim Formu — ERP Posta Rölesi (Y-316)

src/MegaeDefter4Net.MmWeb (mmweb.erp.tr vitrini) /api/contact ucunun gönderim kararı Iletisim/ContactMailer.cs'de: appsettings'teki ErpPosta bölümü (BaseUrl + ApiKey) doluysa talep ERP Posta uygulama rölesine (POST /api/v1/send, amaç contact-form) gider; boşsa eski Smtp bölümü üzerinden doğrudan SMTP ile gönderilir. Karar her istekte güncel ayardan verilir (yeniden başlatma gerekmez); röle hatası (PostaRoleHatasi) SMTP'ye düşürülmez — kullanıcıya kısa Türkçe hata döner, talep EventLog'a da düşer.

Röle yolunda gönderen kutuyu ERP Posta'nın /gonderim-eslesme eşleşmesi belirlediğinden Reply-To taşınmaz; ziyaretçinin e-postası bu yüzden gövdede açıkça yer alır (IletisimFormu.Govde). Alıcılar: Contact:To (zorunlu — boşsa hem röle hem SMTP yolunda erken "Sunucu konfigürasyonu eksik" hatası döner) ve isteğe bağlı Contact:Cc.

Testler: tests/MegaeDefter4Net.Signing.Tests/Web/ContactMailerTests.cs (röle yok → eski yol, Contact:To boş → erken dönüş, röle → doğru uç/amaç/alıcı, röle hatası SMTP'ye düşmez).


Proje Yapısı

mm.erp/
├── src/
│   ├── MegaeDefter4Net.Crypto/         # ISigningKey + XAdES motoru
│   ├── MegaeDefter4Net.Signing/        # EDefterSigner + PfxSigningKey
│   ├── MegaeDefter4Net.SmartCard/      # Pkcs11SigningKey + WindowsCspSigningKey
│   ├── MegaeDefter4Net.Validation/     # DssEquivalentValidator + XAdesEnvelopeValidator + ...
│   └── MegaeDefter4Net.MmWeb/          # mmweb.erp.tr vitrini (/api/contact → ERP Posta rölesi, Y-316)
├── tools/
│   ├── SignerCli/                      # CLI: sign-pfx / sign-p11 / sign-csp / verify
│   └── Saxon/                          # saxon-he.jar (SchematronRunner için)
├── tests/
│   └── MegaeDefter4Net.Signing.Tests/  # xUnit
├── resources/
│   ├── kamusm-trusted-cas.json         # KamuSM kök CA listesi
│   ├── xsd/                            # GİB + XAdES şemaları
│   ├── sch/                            # GİB schematron kuralları
│   └── xslt/                           # Yevmiye / Kebir / Berat HTML görünüm
├── samples/                            # Örnek imzasız / imzalı XML'ler
├── docs/
│   ├── YAPILACAKLAR.md                 # Yaşayan backlog (HSM planı §1)
│   └── hsm-poc-csp.md                  # HSM PoC runbook (KSP yolu)
└── MegaeDefter.Signing.slnx

Build & Test

# Bağımlılıkları çek
dotnet restore

# Tümünü derle
dotnet build -c Release

# Birim testler
dotnet test

Hedef: .NET 10 (net10.0). Bağımlılıklar:

  • BouncyCastle.Cryptography 2.4.0 — RFC3161 timestamp parsing
  • Pkcs11Interop 5.1.2 — USB token PKCS#11 erişimi
  • System.Security.Cryptography.Xml 10.0.12 — XML DSig

Lisans / Telif

İç kullanım. Üretim ortamı kararları ve yorum blokları, gerçek GİB red/kabul karşılaştırmalarına dayanır — kaynak kodu yorumlarındaki tarih + firma + VKN referansları silinmemelidir; bunlar regresyon zırhıdır.