İmzalandıktan sonra değişen imzalı bir PDF otomatik olarak bozuk değildir. ISO 32000-1, bir imzanın üzerinde artımlı güncellemelere izin verir ve bunların yalnızca bazıları imzalayanın belirlediği politikayı bozar. Delphi ve C++Builder için HotPDF Component, bu soruyu her imza sonrası revizyonu sınıflandıran ve DocMDP ile FieldMDP'ye göre notlandıran AnalyzeLoadedSignatureRevisions ile yanıtlar. Senaryo, sözleşme yazılımı gönderen herkese tanıdıktır: müşteriniz bir satın alma sözleşmesini imzalar, gönderir ve eklenmiş bir ek sayfayla geri alır. Okuyucu, imzanın sağlam olduğunu ama belgenin imzalandıktan sonra değiştirildiğini söyleyen sarı bir çubuk gösterir ve odadaki kimse bunun normal bir karşı imza iş akışı mı yoksa birinin sessizce imzalı bir sözleşmeyi düzenlemesi mi olduğunu söyleyemez
İmzadan sonra yasal bir değişiklik sayılan nedir?
Bir değişiklik, anlamsal kategorisi onaylayan imzanın bildirdiği izin içine düştüğünde yasaldır. ISO 32000-1 §12.8.2.2, DocMDP dönüşümünü 1, 2 veya 3 /P değeriyle tanımlar: 1 hiçbir değişikliğe izin vermez, 2 form doldurma ve imzalamaya izin verir, 3 form doldurma, imzalama ve açıklamalara izin verir. HotPDF bunları THPDFDocMDPPermission değerleri dmpNoChanges, dmpFormFillAndSign ve dmpFormFillSignAndAnnotate olarak sunar; dmpNone ise hiçbir DocMDP dönüşümü taşımayan denetim sonuçları için ayrılmıştır
Kategoriler sıralıdır ve bu sıralama tüm kontrolün motorudur. THPDFRevisionModificationLevel, rmlNone, rmlLongTermValidation, rmlFormFillAndSign, rmlAnnotations, rmlOther şeklinde çalışır, kasıtlı olarak daha büyük bir sıra numarasının asla daha az kısıtlayıcı olmayacağı şekilde düzenlenmiştir. Bütün bir belge, imzadan sonraki her revizyonda gözlemlenen maksimum düzeye indirgenir ve DocMDP karşılaştırması tek bir tamsayı testine dönüşür. Erken bir ayrıntı önemlidir: dmpNoChanges'de bile analiz hâlâ rmlLongTermValidation'ı kabul eder. Onaylanmış bir dosyaya DSS ve VRI doğrulama malzemesi veya bir belge zaman damgası eklemek, belgenin değiştirilmesi değil imzanın bakımıdır ve bunu bir ihlal olarak ele almak var olan her uzun vadeli arşivleme iş akışını bozar
HotPDF revizyon zincirini nasıl yeniden inşa eder?
Sezgisel değil, yapısal olarak. ISO 32000-1 §7.5.6'ya göre artımlı bir güncelleme, /Prev'i öncekine işaret eden yeni bir çapraz referans bölümü ekler, dolayısıyla HotPDF startxref'i sondan okur, oradaki bölümü ayrıştırır, /Prev'i geriye doğru takip eder ve tekrarlar, bölümleri en eskiden başlayarak döndürür. Bu döngüde iki güvenlik sınırı bulunur ve her ikisi de başarısız olan bir dosyayı triyaj ederken bilinmeye değerdir: zaten ziyaret edilmiş bir uzaklığa işaret eden bir /Prev, döngüde takılıp kalmak yerine açık bir döngü tanısıyla yürüyüşü sonlandırır ve bin revizyondan uzun bir zincir doğrudan reddedilir. Her ikisi de fonksiyon False döndürerek Analysis.Issue'de yüzeye çıkar ve hiçbiri örtbas edilmemelidir, çünkü döngüsel bir /Prev, sıra dışı bir dosya değil, biçimsiz veya düşman bir dosyadır
Gerçek belgelerde dört tarihsel biçim ortaya çıkar ve dördü de ele alınır: satır satır ayrıştırılan geleneksel xref tabloları, /W ve /Index alanları üzerinden sıkıştırması açılıp çözülen çapraz referans akışları, geleneksel trailer'ı ayrıştırılıp aynı revizyona birleştirilen bir /XRefStm anahtarı taşıyan melez referanslı dosyalar (Office üreticisi durumu, melez çapraz referans akışları üzerine makalede ele alınmıştır) ve bir ObjStm konteynerinin içinde yaşayan nesneler, ki modern bir güncelleme genellikle değişen sözlüğü doğrudan yazmak yerine sıkıştırılmış bir akışa koyduğu için önemlidir, nesne akışları ve artımlı güncellemeler üzerine yazıda anlatıldığı gibi. İmza bölünmeyi çıpalar: /ByteRange[2] + /ByteRange[3], SignedRevisionLength olur ve o uzaklıkta veya ötesindeki her bölüm imza sonrasıdır. Byte aralığının hâlâ doğru şekilde hash'lenip hash'lenmediği ayrı bir sorudur, VerifyLoadedSignature tarafından yanıtlanır ve PDF dijital imzalarını doğrulama üzerine makalede ele alınmıştır
Her değişen nesne nasıl sınıflandırılır?
Sınıflandırma nesne başına çalışır, sonra referanslar boyunca yayılır. İmza sonrası bir bölümün dokunduğu her nesne numarası için HotPDF yeni gövdeyi ve imzalı anlık görüntüde durduğu haliyle gövdeyi okur; özdeş bir gövde rmlNone'dır, çünkü üreticiler nesneleri değiştirmeden yeniden yazarlar. Tanıyıcılar kasıtlı olarak dardır. /Type /DocTimeStamp olan bir nesne, veya /SubFilter'ı ETSI.RFC3161 olan bir nesne, rmlLongTermValidation'dır; katalog /DSS ağacından erişilebilen her şey de öyledir; bir /Type /Sig sözlüğü rmlFormFillAndSign'dır. Konteynerler için test, nesnenin ne olduğu değil hangi anahtarların hareket ettiğidir: katalog yalnızca /DSS, /Extensions veya /AcroForm kazanabilir veya değiştirebilir; AcroForm sözlüğü yalnızca /Fields, /SigFlags, /NeedAppearances, /DR, /DA veya /Q; bir sayfa yalnızca /Annots; bir alan veya widget yalnızca /V, /AP, /AS veya /M. Bu kümelerin dışındaki her şey rmlOther'a düşer, ki eklenmiş ek sayfanın tam olarak nasıl yakalandığı budur: bir sayfa eklemek, hiçbir beyaz listenin kapsamadığı şekillerde sayfa ağacını yeniden düzenler ve hiçbir miktarda meşru form doldurma buna benzemez
Ardından düzeyler yayılır, her konteyner işaret ettiği değişen alt öğelerin maksimum düzeyini miras alır, atama sabitlenene kadar yinelenir. Bu, görünüm akışlarının çalışmasını sağlayan şeydir. Doldurulmuş bir metin alanı /V'yi yeniden yazar ve yeni bir /AP akışına işaret eder ve o akış tek başına, tanınacak hiçbir türü olmayan anonim bir içerik operatörleri kitlesidir; onu sahiplenen alan rmlFormFillAndSign olduğu için akış, rmlOther'a düşmek yerine aynı düzeyi miras alır. Aynı yayılma, aksi takdirde sınıflandırılamayacak sertifika ve iptal akışlarına DSS bağlamını taşır
Okunamayan bir nesne neden bir ihlal olarak sayılır?
Çünkü alternatif, anlamadığı bir şeyi yazarak yenilen bir doğrulayıcıdır. HotPDF'de üç durum itirazsız olarak rmlOther'da sona erer: gövdesi revizyondan okunamayan bir nesne, revizyonun serbest olarak işaretlediği bir nesne ve yukarıdaki tanıyıcıların hiçbiriyle eşleşmeyen bir nesne. Her biri revizyon Issue alanına belirli bir tanı kaydeder, böylece bir operatör hangi nesne numarasının kararı ürettiğini görebilir
Serbest bırakma üçünün en keskinidir. Önceden tanımlanmış bir nesneyi serbest olarak işaretleyen imza sonrası bir revizyon, imzalı bir belgeden içerik silmiştir ve §12.8.2.4 altında hiçbir izin düzeyi buna izin vermez; nesne numaraları FreedObjectNumbers'a düşer ve revizyon rmlOther'a yükseltilir. Okunamayan nesneler farklı bir nedenle aynı mantığı izler. Bir nesneyi ayrıştıramayan bir doğrulayıcının onu zararsız olarak adlandırmak için hiçbir dayanağı yoktur ve buna dürüst yanıt sessizlik değildir. Sıra dışı ama zararsız bir yapıyı bir ihlal olarak raporlamak bir insan incelemesine mal olur; ters hata ise fark edilmemiş bir düzenlemeyle imzalı bir sözleşmeyi gönderir
Delphi'de kararı okumak
Çağrı kısadır. Belgeyi yükleyin, bir imza indeksi seçin, kaydı okuyun; parametresiz aşırı yükleme, belgenin yüklendiği dosyayı yeniden açar ve TStream aşırı yüklemesi, çağıran tarafından sağlanan baytları alır ve dönmeden önce akış konumunu geri yükler. PolicyCompliant, çoğu çağıranın istediği tek boolean'dır ve üç bağımsız kararı birleştirir: izin sözlüklerinin yapısal geçerliliği, DocMDPCompliant ve FieldMDPCompliant. Bileşenleri birleştirmek yerine UI'nızda görünür tutun ve DocMDP dönüşümü olmayan bir belgenin DocMDPCompliant'ı True'da bıraktığını unutmayın, çünkü sıradan bir onay imzası ihlal edilecek hiçbir politika bildirmez ve toplam ModificationLevel o zaman bir karar değil betimleyicidir
var
Pdf: THotPDF;
Analysis: THPDFSignatureRevisionAnalysis;
begin
Pdf := THotPDF.Create(nil);
try
if Pdf.LoadFromFile('contract-countersigned.pdf') > 0 then
begin
if Pdf.AnalyzeLoadedSignatureRevisions(0, Analysis) then
begin
if Analysis.PolicyCompliant then
Writeln('Post-signature changes stay inside the signing policy')
else
Writeln('Policy violation: ', string(Analysis.Issue));
end
else
Writeln('Analysis could not run: ', string(Analysis.Issue));
end;
finally
Pdf.Free;
end;
end;
Triyaj için genellikle özet yerine revizyon başına dökümü istersiniz, çünkü bu, belge geçmişinde işlerin ne zaman ters gittiğini söyler. Analysis.Revisions'daki her giriş, zincirdeki indeksini, yazıldığı çapraz referans uzaklığını, kendi değişiklik düzeyini ve ilgili nesne numaralarını taşır
const
LevelNames: array[THPDFRevisionModificationLevel] of string =
('none', 'long-term validation', 'form fill and sign',
'annotations', 'other');
var
I: Integer;
begin
Writeln(Format('%d revisions in chain, signature sits at index %d',
[Analysis.TotalRevisionCount, Analysis.SignedRevisionIndex]));
for I := 0 to High(Analysis.Revisions) do
Writeln(Format(' rev %d at offset %d: %s (%d changed, %d freed) %s',
[Analysis.Revisions[I].RevisionIndex,
Analysis.Revisions[I].XRefOffset,
LevelNames[Analysis.Revisions[I].ModificationLevel],
Length(Analysis.Revisions[I].ChangedObjectNumbers),
Length(Analysis.Revisions[I].FreedObjectNumbers),
string(Analysis.Revisions[I].Issue)]));
end;
FieldMDP ayrı olarak değerlendirilir ve bu kasıtlıdır
Bir belge DocMDP'yi karşılayabilir ve yine de meşru olmayabilir, bu yüzden FieldMDPCompliant, düzey karşılaştırmasına katlanmak yerine ayrı bir boolean'dır. ISO 32000-1 §12.8.2.4, FieldMDP dönüşümünü ve §12.7.5.5 ilgili /SigFieldLock girişini, belge bir bütün olarak hâlâ form doldurmaya izin verse bile, adlandırılmış form alanlarını imzalanma anında dondurmak için tanımlar. Bir alanı doldurmak seviye 2 bir eylemdir; imzalayanın kilitlediği bir alanı doldurmak, seviyeden bağımsız olarak bir ihlaldir. HotPDF, kapsamı THPDFFieldLockAction olarak flaAll, flaInclude veya flaExclude şeklinde okur, kilit politikası taşımayan sonuçlar için flaNone vardır, ve isimleri Permissions.FieldNames'e okur: flaAll her şeyi kilitler, flaInclude listelenen isimleri kilitler, flaExclude onlar dışındaki her şeyi kilitler. Sonuçları okurken önemli bir ayrıntı, yalnızca imzalı anlık görüntüde zaten mevcut olan alanların ChangedFieldNames'de raporlanmasıdır, çünkü imzalamadan tamamen sonra oluşturulmuş bir alanın çelişecek imzalı bir durumu yoktur ve bunun yerine DocMDP yolu tarafından yakalanır
var
Source: TFileStream;
Analysis: THPDFSignatureRevisionAnalysis;
I: Integer;
begin
Source := TFileStream.Create('contract.pdf', fmOpenRead or fmShareDenyWrite);
try
if Pdf.AnalyzeLoadedSignatureRevisions(0, Source, Analysis) then
if Analysis.Permissions.HasFieldMDP and (not Analysis.FieldMDPCompliant) then
for I := 0 to High(Analysis.ChangedFieldNames) do
Writeln('modified after locking: ',
string(Analysis.ChangedFieldNames[I]));
finally
Source.Free; // stream position was restored before the call returned
end;
end;
Bu analiz size neyi söylemeyecek
Bir imzayı doğrulamaz. AnalyzeLoadedSignatureRevisions, yapı ve izinler hakkında akıl yürütür; imzalı byte aralığının hâlâ CMS blob'undaki değere hash'lenip hash'lenmediği ve imzalayan sertifikasının güvendiğiniz herhangi bir şeye zincirlenip zincirlenmediği, VerifyLoadedSignature ve VerifyLoadedSignatureWithTrust tarafından yanıtlanır. Bir dosya mükemmel şekilde politikaya uyumlu ve kriptografik olarak değersiz olabilir, dolayısıyla iki kontrol de herhangi bir gerçek kabul kapısında yan yana durmalıdır. Ayrıca içerik akışlarının içindeki niyeti okumaz: içerik akışı toptan değiştirilmiş bir sayfa, beyaz listenin dışında bir değişiklik olarak yakalanır, ama analiz size değişikliğin bir ödeme rakamını değiştirdiğini söylemez. Bir rmlOther kararı, dolandırıcılık olduğu değil bir insanın bakması gerektiği anlamına gelir ve uyumlu bir karar, değişikliğin istendiği değil izin verilen bir kategoriye uyduğu anlamına gelir. Revizyon yürüyüşü olmadan yalnızca imzalayanın bildirdiğine ihtiyacınız olduğunda, GetLoadedSignaturePermissions politika sözlüklerini tek başına döndürür
Burada anlatılan her şey, döngüde harici bir imzalama hizmeti olmadan Delphi ve C++Builder'da yerel olarak çalışır, ki bu da onu yalnızca birinin zaten şüphelendiği belgeler yerine her gelen belgede çalıştırmayı pratik kılan şeydir. Eşleştiği izin ve doğrulama yöntemleri dahil tam imza ve revizyon API'si, Delphi ve C++Builder için HotPDF Component'in bir parçasıdır