İmzalı bir PDF aldınız ve görüntüleyicinizde bunu kimin imzaladığını, ne zaman imzaladığını, imzanın dosyanın tamamını kapsayıp kapsamadığını ve uzun vadeli uyumluluğa ne kadar yakın olduğunu göstermeniz gerekiyor. Delphi ve Lazarus için PDFium Bileşeni, salt okunur çağrılarla bu dört soruyu da yanıtlar: FPDF_GetSignatureCount ve FPDFSignatureObj_* ailesi imza sözlüğünü ortaya çıkarır ve TPdf.ValidatePades PAdES temel seviyesini sınıflandırır. Bu, PDFium ile PDF imzaları üzerine üç makaleden ilkidir; bunu takip eden iki makale B-B imza oluşturmayı ve uzun vadeli zaman damgaları eklemeyi kapsar. Yine de başlangıçta bir sınır çizilmelidir: buradaki her şey incelemedir ve bir imzanın ne iddia ettiğini okumak, şifrelemesini doğrulamaktan veya imzalayana güvenip güvenmeyeceğinize karar vermekten farklı bir iştir
Bir PDF imza sözlüğü neden sadece bir bayt yığını değildir?
Bir PDF imzası opak bir ek değil, bir sözlüktür ve en önemli iki girdisi size dosyanın gerçekte ne kadarının korunduğunu söyler. ISO 32000-1 §12.8, imza sözlüğünü bir /ByteRange ve bir /Contents girdisiyle tanımlar. /Contents, imzalayan sertifikasını, imzalı öznitelikleri ve imza değerinin kendisini taşıyan kriptografik zarf olan hex kodlu bir CMS SignedData yapısını (RFC 5652) tutar. /ByteRange ise geliştiricilerin küçümsediği kısımdır: /Contents hex dizesi hariç tüm dosyayı kapsayan iki adet uzaklık-uzunluk (offset-length) aralığından oluşan bir dizidir. Bu boşluk, imza baytlarının oturduğu yerdir ve her iki taraftaki iki aralık, imzanın taahhüt ettiği şeydir
ByteRange tasarımı, artımlı kaydetmeyi denetlenebilir kılan şeydir. Bir imzalayan henüz var olmayan imza baytlarını özetleyemeyeceğinden (hash), dosya /Contents yer tutucusunun etrafından bölünür ve diğer her şey imza içine özetlenir. ByteRange aralığı dosyanın sonuna ulaşmayan bir imza bir uyarı işaretidir: kapsanan aralıktan sonra, daha sonraki bir artımlı güncelleme yoluyla eklenen içerik, okuyucunun gördüğü şeyi değiştirse bile imzayı bozmaz. Dolayısıyla ciddi bir müfettişin kontrol ettiği ilk şey kimin imzaladığı değil, imzanın onaylıyor göründüğü baytları kapsayıp kapsamadığıdır
PDFium'un salt okunur API'si ile imza sözlüğünü okuma
PDFium Bileşeni, imza sözlüğünü iki salt okunur üye aracılığıyla yüzeye çıkarır: SignatureCount ve Signature[Index] kaydı. Arka planda FPDF_GetSignatureCount, FPDF_GetSignatureObject ve /SubFilter, /ByteRange, /Contents, /Reason ve imzalama zamanı için FPDFSignatureObj_* erişimcilerini çağırırlar. Salt okunur burada geçerli ifadedir: PDFium imzaları listeleyebilir ve okuyabilir ancak bir imza oluşturmak veya yazmak için bir API'ye sahip değildir; bu serinin imzalama tarafının PDFium yerine kütüphanenin kendisi tarafından uygulanmasının nedeni budur
var
Pdf: TPdf;
i: Integer;
Sig: TPdfSignature;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'contract-signed.pdf';
Pdf.Active := True;
for i := 0 to Pdf.SignatureCount - 1 do
begin
Sig := Pdf.Signature[i];
Writeln('SubFilter : ', Sig.Encoding); // ETSI.CAdES.detached, adbe.pkcs7.detached, ...
Writeln('Signed at : ', Sig.Time); // signer date string, e.g. D:20260708120000+02'00'
Writeln('Reason : ', Sig.Reason);
Writeln('CMS length: ', Length(Sig.Content)); // raw DER SignedData taken from /Contents
Writeln('DocMDP : ', Sig.Permission); // 0 = no certification, 1..3 = MDP level
end;
finally
Pdf.Free;
end;
end;
Her TPdfSignature kaydı doğrudan sözlüğe eşlenir. Encoding en teşhis edici alan olan /SubFilter'dır; çünkü imza işleyicisini adlandırır ve modern bir ETSI.CAdES.detached imzasını eski veya yasaklanmış olandan hemen ayırır. Time, yazar tarafından beyan edilen imzalama zamanıdır ve imzalayanın saati tarafından iddia edilen bir PDF tarih dizesidir, güvenilir zaman değildir. Content, /Contents'ten alınan ham DER SignedData'dır ve Permission, DocMDP sertifikasyon seviyesini ortaya çıkarır (sıradan bir onay imzası için 0, daha sonraki değişiklikleri kilitleyen bir sertifikasyon imzası için 1 ila 3). Kaydın yüzeye çıkarmadığı tek alan ayrıştırılmış ByteRange aralığıdır ve bu atlama bilinçlidir; çünkü ValidatePades, bunu elle yeniden yapmanızı gerektirmek yerine sizin için ByteRange kapsama aritmetiğini yapar
PAdES B-B, B-T, B-LT ve B-LTA arasındaki fark nedir?
Dört PAdES temel seviyesi, minimum düzeyde geçerli bir imzadan onlarca yıllık arşivlemede hayatta kalacak şekilde oluşturulmuş bir imzaya kadar bir merdiven oluşturur ve her seviye kesinlikle altındaki seviyeyi içerir. ETSI EN 319 142-1 bunları B-B, B-T, B-LT ve B-LTA olarak tanımlar. B-B (Basic - Temel) imza artı zorunlu imzalı özniteliklerdir ve daha fazlası değildir. B-T (Timestamp - Zaman Damgası), imza üzerine güvenilir bir RFC 3161 zaman damgası ekler; böylece imzalama anı, imzalayanın saati tarafından iddia edilmek yerine bir zaman damgası yetkilisi tarafından onaylanır. B-LT (Long-Term - Uzun Vadeli), doğrulama materyalini — sertifika zincirini ve isteğe bağlı olarak OCSP veya CRL yanıtlarını — dosyanın içine gömer; böylece imza, düzenleyen altyapı ortadan kalktıktan yıllar sonra bile doğrulanabilir. B-LTA (Long-Term with Archive timestamp - Arşiv zaman damgalı Uzun Vadeli), bu materyali bir belge zaman damgasıyla sararak uzun vadeli verilerin kendisini korur ve temel şifreleme eskimeden önce yeniden zaman damgası vurmanız için size bir nokta sunar
Pratik okuma zaman ufku ile ilgilidir. Bir B-B imzası "bunu biri imzaladı mı" sorusunu yanıtlar. B-T, "ve ne zaman, kanıtlanabilir şekilde" sorusunu yanıtlar. B-LT, "ve sertifikaların süresi dolduktan sonra bunu hala kontrol edebilir miyim" sorusunu yanıtlar. B-LTA ise "ve bu kontrol yirmi yıl sonra hala geçerli olacak mı" sorusunu yanıtlar. Düzenleyici profiller bir basamak seçer: birçok e-fatura ve eIDAS bağlamı en azından B-T gerektirir ve arşivleme zorunlulukları B-LT veya B-LTA'ya ulaşır. Bir belgenin kabul etmeden veya reddetmeden önce gerçekte hangi basamağa ulaştığını bilmek, inceleme adımının tüm amacıdır
TPdf.ValidatePades ile temel seviyeyi algılama
PDFium Bileşeni tüm seviye sorusunu tek bir çağrıya indirger. TPdf.ValidatePades, Level alanı bir TPadesLevel — plNone, plUnknown, plB_B, plB_T, plB_LT veya plB_LTA — olan bir TPadesValidationResult kaydı döndürür; bunun yanında bir dizi sorun, bir imza sayısı ve bir belge zaman damgası sayısı sunar. Seviye monotonik olarak çıkarılır: doğrulayıcı önce B-B'yi kurar, ardından bir imza zaman damgası veya bir belge zaman damgası varsa B-T'ye, katalog sertifikalarla birlikte bir /DSS ve /Extensions /ESIC Seviye 1 işaretçisi taşıyorsa B-LT'ye og bir belge zaman damgası ile ESIC Seviye 2 işaretçisi birlikte mevcutsa B-LTA'ya yükseltir. Sonucu eyleme dönüştürülebilir kılan iki yardımcı vardır: IsCompliant yalnızca seviye en az B-B'ye ulaştığında ve sorun kümesi boş olduğunda True olur, IsCompliantAt ise plB_T gibi bir politika taban belirlemenize olanak tanır
var
Pdf: TPdf;
R: TPadesValidationResult;
begin
Pdf := TPdf.Create(nil);
try
Pdf.FileName := 'contract-signed.pdf';
Pdf.Active := True;
R := Pdf.ValidatePades;
case R.Level of
plNone: Writeln('No PAdES signature present');
plUnknown: Writeln('Signature present but level undeterminable');
plB_B: Writeln('PAdES B-B (basic)');
plB_T: Writeln('PAdES B-T (trusted timestamp)');
plB_LT: Writeln('PAdES B-LT (long-term material embedded)');
plB_LTA: Writeln('PAdES B-LTA (archive timestamp)');
end;
Writeln('Signatures : ', R.SignatureCount);
Writeln('DocTimeStamps: ', R.DocTimeStampCount);
if R.IsCompliantAt(plB_T) then
Writeln('Meets the B-T policy floor')
else
Writeln('Below the required B-T level');
finally
Pdf.Free;
end;
end;
adbe.pkcs7.sha1 neden yasaklanmış bir SubFilter'dır?
Çünkü SHA-1 bozuktur ve adbe.pkcs7.sha1 işleyicisi bunu içine yerleştirir. Bu SubFilter, PKCS#7'ye sarmalamadan önce belgeyi SHA-1 ile önceden özetler og SHA-1 yıllardır çakışmalara karşı savunmasızdır; bu nedenle ETSI EN 319 142-1 madde 6.3 temel bir imza için bunu tamamen yasaklar. ValidatePades, adbe.pkcs7.sha1 veya adbe.x509.rsa_sha1 gördüğünde ppeiForbiddenSubFilter hatası verir ve CMS'nin kendisi mesaj özeti olarak MD5 veya SHA-1 kullandığında (madde 6.2.1) ppeiBadDigestAlgorithm hatası verir. Bunlar, aynı zayıflık sınıfını iki farklı katmanda yakalayan iki ayrı kontroldür
Sorun kümesinin toplamda 26 üyesi vardır ve en sık karşılaşacağınız olanlar yapı ve kapsam etrafında kümelenir. ppeiByteRangeNotCoveringFile, daha önce açıklanan kapsam kontrolüdür. ppeiForbiddenCertKey, imza sözlüğü bir /Cert girdisi taşıdığında tetiklenir; PAdES bunu yasaklar çünkü zincir bunun yerine CMS SignedData.certificates içinde yaşamalıdır. ppeiMissingSigningCertificate, ppeiMissingContentType ve ppeiMissingMessageDigest eksik olan zorunlu imzalı öznitelikleri işaretler ve ppeiDetachedContentViolation imzalanan içeriği ayırmak yerine yanlışlıkla gömen bir imzayı yakalar. Kümeyi listelemek, çıplak bir reddi günlüğe kaydedebileceğiniz bir teşhise dönüştürür
var
R: TPadesValidationResult;
Issue: TPadesValidationIssue;
begin
R := Pdf.ValidatePades;
if R.Issues <> [] then
for Issue := Low(TPadesValidationIssue) to High(TPadesValidationIssue) do
if Issue in R.Issues then
Writeln('Issue: ',
GetEnumName(TypeInfo(TPadesValidationIssue), Ord(Issue)));
end;
ValidatePades neleri kontrol etmez?
ValidatePades güveni değil yapıyı doğrular ve birini diğeriyle karıştırmak tehlikeli bir hatadır. plB_LTA sonucu, belgenin doğru yerlerde yetkilendirilmiş tüm öznitelikler, materyaller ve zaman damgaları ile iyi biçimlendirilmiş bir B-LTA imzası içerdiği anlamına gelir — imzanın kriptografik olarak geçerli olduğu, sertifika zincirinin güvendiğiniz bir köke bağlandığı veya zincirdeki hiçbir sertifikanın iptal edilmediği anlamına gelmez. Doğrulayıcı kasıtlı olarak kriptografik doğrulama yapmaz: imzayı ByteRange üzerinden yeniden hesaplamaz, güven zincirini oluşturup değerlendirmez ve OCSP veya CRL iptal durumunu kontrol etmez. Bu ayrım bilinçlidir ve yararlıdır; çünkü yapısal inceleme hızlıdır, tamamen deterministiktir og hiçbir anahtar, ağ ve platform kriptosu gerektirmez; bu nedenle ValidatePades Windows, Linux ve macOS üzerinde dosya baytları üzerinden saf Pascal olarak aynı şekilde çalışır. Güven zinciri doğrulaması ise aksine politikadan ayrılamaz — hangi köklere güvendiğiniz, iptal bilgisini nasıl getirdiğiniz, zaman damgası toleransınızın ne kadar katı olduğu — og platform sertifika deposuna bağlı olan daha sonraki bir aşamaya aittir; bu nedenle geçen bir ValidatePades işlemini bir imzanın doğru şekilde şekillendirildiğine dair gerekli geçiş olarak değerlendirin, ardından güvenmeden önce yapısal olarak sağlam bir imzayı gerçek kriptografik doğrulamaya teslim edin
Bu yapısal geçiş başlamak için doğru yerdir og PDFium Bileşeni ile PDF güvenlik risklerini denetleme ve baskıya hazır PDF/X belgelerini doğrulama gibi daha geniş salt okunur kontrollerle doğal olarak eşleşir. Bir imzayı okuyup sınıflandırabildikten sonraki adım bir imza üretmektir: bu serideki ikinci makale PDFium VCL ile PDF'leri imzalama konusunu kapsar ve üçüncüsü bunu güvenilir zaman damgalarına ve uzun vadeli B-LT ve B-LTA imzalarına genişletir. Burada gösterilen salt okunur imza incelemesi ve ValidatePades sınıflandırıcısı Delphi, C++Builder ve Lazarus için PDFium Bileşeni'nin bir parçasıdır