Teknik Makale

DocMDP ve FieldMDP: Delphi'de PDF Revizyonlarını Denetleme

İ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 /SubFilterETSI.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 DocMDPCompliantTrue'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