HotPDF, yüklenen PDF belgelerindeki dijital imzaları, v2.259.0 sürümünde sunulan üç THotPDF yöntemiyle doğrular: GetLoadedSignatureInfo, VerifyLoadedSignature ve VerifyLoadedSignatureEx. Bileşen, orijinal dosyanın /ByteRange bölümlerini yeniden hesaplar, CMS messageDigest özniteliğini kontrol eder ve gömülü imzalayan sertifikasına karşı bir RSA PKCS#1 v1.5 doğrulaması çalıştırarak, belge baytları bozulmadığında svValid döndürür
Senaryo sıradan, ancak tehlike büyüktür. Bir karşı taraf imzalı bir sözleşmeyi iade eder, iş akışınızın bunu arşivlemesi gerekir ve birisi tek önemli soruyu sorar: bu bize gönderilen, iddia ettiği sertifikayla imzalanmış, bayt bayt aynı olan belge mi? Kod içinde buna cevap vermek, imza hikayesinin doğrulama tarafıdır; imzalama tarafı, yani en başta PAdES imzalarını oluşturma ve gömme işlemi, HotPDF ile PAdES dijital imzaları oluşturma hakkındaki tamamlayıcı makalede ele alınmıştır. Bu makale ise diğer yönü hakkındadır: imzalanmış olarak gelen bir PDF ve siz Acrobat'ın yeşil onay işaretinin ekran görüntüsü yerine programlı bir karar istiyorsunuz
İmzalı bir PDF kurcalanmadığını nasıl kanıtlar?
Bir PDF imzası, belgenin soyut bir "belge" kavramını değil, dosyanın belirli bayt aralıklarını korur. ISO 32000-1 §12.8 mekanizmayı tanımlar: imza form alanı, /Contents girdisi bir CMS SignedData kapsayıcısı (RFC 5652) tutan ve /ByteRange dizisi §12.8.1 uyarınca imzanın kapsadığı dosya bölgelerini adlandıran bir sözlük taşır. Dizi, ofset ve uzunluk çiftlerinin bir listesidir, pratikte iki bölümdür: /Contents hex dizesinden önceki her şey ve ondan sonraki her şey. İmza değeri kendisini kapsayamaz, bu nedenle dosya bu deliğin etrafından karma işlemine (hash) tabi tutulur
Bu tasarımın, tüm API'yi şekillendiren bir sonucu vardır: doğrulama işlemi, diskte tam olarak durdukları şekliyle orijinal serileştirilmiş baytları karma işlemine tabi tutmalıdır. Ayrıştırılmış bir nesne modeli bunun için kullanışsızdır, çünkü değişmemiş bir belgeyi bile yeniden serileştirmek farklı baytlar üretir. HotPDF bu nedenle belgenin yüklendiği kaynak dosyaya karşı veya sizin sağladığınız ham baytlardan oluşan bir TStream nesnesine karşı doğrulama yapar, asla bellek içi temsiline karşı yapmaz
Herhangi bir şeyi doğrulamadan önce imza meta verilerini okuma
GetLoadedSignatureInfo, tek bir belge baytına dokunmadan imza sözlüğünü ve onun CMS kapsayıcısını ayrıştırır; bu, yalnızca kimin ne zaman imzaladığını görüntülemeniz gerektiğinde en doğru ilk çağrıdır. İmza alanları form alanı sırasında 0'dan itibaren indekslenir ve GetLoadedSignatureFieldCount kaç adet imza alanı olduğunu söyler. Dönen THPDFSignatureInfo kaydı; alan adını, /SubFilter değerini, imzalayan sertifikasının ortak adını (CN), konu ve veren ayırt edici adlarını (DN), seri numarasını, geçerlilik tarihlerini, imzalama zamanını (mevcut olduğunda imzalı öznitelikten, yoksa sözlüğün /M girişinden), özet algoritma adını ve /Reason, /Location ve /ContactInfo dizelerini taşır. Status üyesi, "ayrıştırıldı, doğrulanmadı" şeklindeki dürüst etiket olan svNotVerified olarak kalır
var
Pdf: THotPDF;
Info: THPDFSignatureInfo;
I: Integer;
begin
Pdf := THotPDF.Create(nil);
try
Pdf.LoadFromFile('signed-contract.pdf');
for I := 0 to Pdf.GetLoadedSignatureFieldCount - 1 do
begin
Info := Pdf.GetLoadedSignatureInfo(I);
Writeln('Field: ', Info.FieldName);
Writeln('Signer: ', Info.SignerName);
Writeln('Issuer: ', Info.IssuerDN);
Writeln('Algorithm: ', Info.HashAlgorithm);
Writeln('SubFilter: ', Info.SubFilter);
end;
finally
Pdf.Free;
end;
end;
Kriptografik kontrolün çalıştırılması
VerifyLoadedSignatureEx, dosya yüklü bir belge için tam doğrulamayı gerçekleştirir ve doldurulmuş bilgi kaydını tek bir çağrıda geri verir: kaynak dosyayı yeniden açar, /ByteRange bölümlerini SignerInfo özet algoritmasıyla karma işlemine tabi tutar, sonucu messageDigest imzalı özniteliğiyle (RFC 5652 §5.4) karşılaştırır ve ardından imzalı özniteliklerin DER SET yeniden kodlaması üzerinde RSA doğrulaması yapar. Bir imza imzalı öznitelikler taşımadığında, RSA kontrolü doğrudan belge özeti üzerinde çalışır. Desteklenen imzalar; ana akım imzalama araçları tarafından üretilen adbe.pkcs7.detached ve ETSI.CAdES.detached alt filtrelerini kapsayan SHA-1, SHA-256, SHA-384 veya SHA-512 özetlerine sahip RSA PKCS#1 v1.5 imzalarıdır
var
Status: THPDFSignatureVerifyStatus;
Info: THPDFSignatureInfo;
begin
Status := Pdf.VerifyLoadedSignatureEx(0, Info);
case Status of
svValid:
if Info.CoversWholeDocument then
Writeln('Valid; signature covers the whole file')
else
Writeln('Valid; file was extended after signing');
svDigestMismatch:
Writeln('Document bytes changed after signing');
svSignatureInvalid:
Writeln('RSA check failed over signed attributes');
svUnsupportedAlgorithm:
Writeln('Non-RSA key or unknown digest algorithm');
svMalformed:
Writeln('CMS container could not be parsed');
svSourceUnavailable:
Writeln('No source bytes; use the TStream overload');
end;
end;
Dışarıdan gizemli görünen başarısızlıkları açıkladığı için iki uygulama detayı bilinmeye değerdir. İlk olarak, imzalı öznitelikler kontrolü kodlama konusunda titizdir: dosyanın içinde öznitelikler [0] IMPLICIT olarak etiketlenmiştir, ancak imza bunların DER SET OF biçimi üzerinde hesaplanmıştır, bu nedenle doğrulayıcı karma işleminden önce tam olarak RFC 5652 §5.4'ün gerektirdiği şekilde yeniden etiketleme yapar. Dosyada göründükleri şekliyle baytları karma işlemine tabi tutan el yapımı bir doğrulayıcı, düzgün şekilde imzalanmış her belgeyi reddedecektir. İkinci olarak, /Contents geleneksel olarak ayrılmış bir bayt bütçesine göre sıfırla doldurulur, bu nedenle doğrulayıcı ayrıştırmadan önce DER bloğunu dış SEQUENCE yapısının gerçek uzunluğuna göre keser; çöp gibi görünen sondaki sıfırlar bozulma değil, normaldir. Sertifika içe aktarma tarafındaki aynı ASN.1 ayrıştırma tehlikeleri ailesi, HotPDF in PKCS#12 ve ASN.1 güvenlik sıkılaştırması makalesinin konusudur
Geçerli bir imza aslında neyi garanti eder?
svValid tam olarak şu anlama gelir: /ByteRange tarafından adlandırılan baytlar imzalayanın imzaladığı değere karma edilir ve imza, CMS kapsayıcısına gömülü sertifikanın ortak anahtarı altında doğrulanır. Bu, bayt bütünlüğü artı anahtar bağlamadır ve başka hiçbir şey değildir. Sertifika zinciri ve güven doğrulaması HotPDF'in doğrulayıcısı için açıkça kapsam dışıdır: zinciri bir köke yürümez, iptali kontrol etmez veya herhangi bir güven deposuna danışmaz. Değiştirilmiş bir belgeyi yeniden imzalayan bir saldırganın kendinden imzalı sertifikası svValid olarak doğrulanacaktır, çünkü matematik kendi içinde tutarlıdır. İmzalayanın iddia ettiği kişi olup olmadığı ve kimsenin onlara güvenip güvenmeyeceği, kuruluşunuzun sertifika beyaz listesi, Windows sertifika deposu veya bir doğrulama makamı olsun, ayrı bir katmana ait bir politika kararıdır
CoversWholeDocument bayrağı daha ince bir boşluğu korur. Bir imza yalnızca /ByteRange alanını kapsar ve PDF'in artımlı güncelleme mekanizması, bir imzadan sonra imzanın geçerliliğini bozmadan içerik eklenmesine izin verir; bu tasarım gereğidir ve çoklu imza iş akışları bu şekilde çalışır. Bayrak doğrulama sırasında hesaplanır ve yalnızca iki bölüm artı /Contents boşluğu dosyanın tamamını kapsadığında doğru olur. svValid sonucu CoversWholeDocument false olarak geldiğinde, imzalanan revizyon bozulmamıştır ancak dosya daha sonra eklemeler içermektedir ve bu eklemelerin neleri değiştirdiği, iş akışınızın tolere edip etmeyeceğine karar vermesi gereken bir durumdur
Akışla yüklenen ve şifrelenmiş belgelerin kendi kaynak baytlarına ihtiyacı vardır
Parametresiz VerifyLoadedSignature ve VerifyLoadedSignatureEx, bileşenin belgenin hangi dosyadan geldiğini hatırlamasına bağlıdir. Belgeyi bir akıştan yüklerseniz, yeniden açılacak bir dosya adı yoktur; aynısı şifreli belgeler için kullanılan parola yeniden yükleme yolu, yani HotPDF ile AES-256 PDF şifreleme makalesinde açıklanan iş akışı için de geçerlidir. Her iki durumda da, dosya tabanlı aşırı yüklemeler tahmin yürütmek yerine svSourceUnavailable döndürür. Çözüm, orijinal ham baytları sakladığınız yerden, hala elinizde olan bir dosyadan, bir bellek arabelleğinden veya bir veritabanı blobundan teslim etmenizi sağlayan TStream aşırı yüklemesidir
var
Src: TFileStream;
Status: THPDFSignatureVerifyStatus;
Info: THPDFSignatureInfo;
begin
// Stream-loaded document: the component holds no source
// file name, so supply the original bytes yourself.
Src := TFileStream.Create('signed-contract.pdf',
fmOpenRead or fmShareDenyWrite);
try
Status := Pdf.VerifyLoadedSignature(0, Src, Info);
if Status <> svValid then
Writeln('Verification failed: ', Ord(Status));
finally
Src.Free;
end;
end;
Doğrulayamadığınız şeylerin bildirilmesi
Yalnızca "geçerli" ve "geçersiz" durumlarını bilen bir doğrulayıcı, yalnızca anlamadığı belgeleri yanlış raporlayacaktır, bu nedenle durum listesi kullanıcı arayüzünüzün ayırt etmesi gereken durumları ayırır. svDigestMismatch, imzadan sonra belge baytlarının değiştiği anlamına gelir ki bu klasik bir kurcalama sinyalidir. svSignatureInvalid, baytların doğru şekilde karma edildiği ancak RSA kontrolünün başarısız olduğu anlamına gelir, bu da bozuk veya sahte bir imza değerine işaret eder. svUnsupportedAlgorithm, ECDSA anahtarları ve tanınmayan özetler için dürüst bir yanıttır: imza mükemmel derecede iyi olabilir, HotPDF basitçe kontrol edemez ve bunu "geçersiz" olarak raporlamak sağlıklı bir belgeye iftira atmak olur. svMalformed, hiçbir şekilde ayrıştırılamayan bir CMS kapsayıcısını işaretler. Geçit tarzı kontroller için, VerifyAllLoadedSignatures yalnızca en az bir imza alanı mevcut olduğunda ve her biri svValid olarak doğrulandığında doğru değerini döndürür; bu, bundan daha azını reddeden bir arşiv alım iş hattı için kullanışlı tek bir booleandır
İmza doğrulama, PAdES imzalama, AES-256 şifreleme ve yüklenen belge düzenleme API'si; harici DLL bağımlılığı olmadan Delphi ve C++Builder için aynı yerel VCL kütüphanesinde sunulur; tam özellik listesi ve desteklenen IDE sürümleri HotPDF Bileşeni ürün sayfasındadır