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 8 ü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).
- Üç farklı anahtar kaynağını (USB token, 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 façade) │
│ • 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 │ Windows sertifika dep. │ Geliştirme/test │
│ akisp11.dll │ CryptoAPI / CNG (RDP) │ PFX dosyası │
└──────────────────┴────────────────────────┴─────────────────┘
| Katman | Proje | Açıklama |
|---|---|---|
| Kripto çekirdek | MegaeDefter4Net.Crypto |
ISigningKey, XAdesBesSigner, c14n + digest |
| Yüksek seviye façade | MegaeDefter4Net.Signing |
EDefterSigner, PfxSigningKey |
| Donanım anahtarları | MegaeDefter4Net.SmartCard |
Pkcs11SigningKey, WindowsCspSigningKey |
| 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 |
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 |
akisp11.dll üzerinden doğrudan USB token |
Üretim — özel anahtar token'dan çıkmaz |
WindowsCspSigningKey |
Windows sertifika deposu (CSP/CNG) | 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 |
4. Façade — EDefterSigner
src/MegaeDefter4Net.Signing/EDefterSigner.cs
İki kritik küçük iş yapar:
SignFile(input, output)— dosya tabanlı kullanım kolaylığı.- CR/LF normalize — XML 1.0 §2.11 uyarınca
CRveCRLF'leriLF'ye çevirir. Aksi halde C14N
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ış:
- Skeleton kur —
ds:Signatureiçinde boşds:Object → xades:QualifyingProperties → SignedProperties(SigningTime,SigningCertificate,ClaimedRole,DataObjectFormat). - Pretty-print — hash öncesi whitespace yerleştir (sonra c14n bunu imzaya dahil eder, verify aynı bytları görür).
- Reference 1 digest —
SignedProperties→ c14n → SHA-256. - Reference 2 digest — Tüm belge,
enveloped-signaturetransform uygulandıktan sonra c14n → SHA-256. - SignedInfo inşa et — iki Reference,
SignatureMethodURI,CanonicalizationMethod=xml-c14n-20010315(plain, WithComments DEĞİL — Ma3Api kuralı). - SignedInfo'yu c14n'le ve imzala —
ISigningKey.SignData(...). ECDSA için P1363 → DER dönüşümü — GİB Java validatörü DER bekler. SignatureValue— base64 olarak yerleştir.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ı.X509IssuerNameboşluksuz format (CN=...,OU=...) — binary cert encoding'iyle eşleşir.xmlns:dsvexmlns:xadesyerel'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 kontrolIdentificationOfSigningCertificate—SigningCertificatereferansı, cert digestValidationContextInitialization— sertifika zinciri inşasıX509CertificateValidation— KamuSM kök CA listesine karşı zincir doğrulamaCryptographicVerification— Reference digest'leri +SignedInfoimza doğrulamaSignatureAcceptanceValidation—ClaimedRole,SigningTime, MimeType kontrolleriAlgorithmObsolescenceValidation— 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ı |
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) Doğrulama (hızlı)
var doc = new XmlDocument { PreserveWhitespace = true };
doc.Load("out.signed.xml");
bool ok = XAdesBesSigner.Verify(doc);
// 4) 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
# Windows cert store ile imzala (RDP)
dotnet run --project tools/SignerCli -- sign-csp \
--in yev.xml --out yev.signed.xml --vkn 1234567890
# 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.
Proje Yapısı
mm.erp/
├── src/
│ ├── MegaeDefter4Net.Crypto/ # ISigningKey + XAdES motoru
│ ├── MegaeDefter4Net.Signing/ # EDefterSigner + PfxSigningKey
│ ├── MegaeDefter4Net.SmartCard/ # Pkcs11SigningKey + WindowsCspSigningKey
│ └── MegaeDefter4Net.Validation/ # DssEquivalentValidator + XAdesEnvelopeValidator + ...
├── 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
└── 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 8 (net8.0). Bağımlılıklar:
BouncyCastle.Cryptography 2.4.0— RFC3161 timestamp parsingPkcs11Interop 5.1.2— USB token PKCS#11 erişimiSystem.Security.Cryptography.Xml 8.0.2— 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.