Teknik Makale

Delphi'de FieldMDP ile Kilitli Form Alanı Düzenleme Saptama

PDFlibPas, FieldMDP sorusunu TPDFlib.GetSignatureModificationVerdict üzerinden yanıtlar. İmzalı bir PDF, parolası ve bir imza alanı adı verildiğinde metot, o imzadan sonra eklenen her artımlı güncellemeyi gezinir, bu güncellemelerin dokunduğu her form alanının tam nitelikli adını (fully qualified name) çözer; FieldMDP tarafından kilitlenmiş bir alan değiştirildiyse 3, yalnızca kilitsiz alanlara dokunulduysa 2 döndürür. GetSignatureModificationDetail ise revizyonu ve alanı adıyla raporlar

Bu yanıtı gerektiren iş akışı çok onaylı formdur. Talep eden kişi satın alma talebinde Order.Customer ile Order.Total alanlarını doldurur ve Approval.Requester alanını imzalayarak o iki alanı kilitler. Bir yönetici maliyet merkezi ekler ve Approval.Manager alanını imzalar. İki hafta sonra biri dosyayı bir masaüstü düzenleyicide açar ve üçüncü artımlı güncellemede Order.Total değerini yukarı çeker. Her imza yalnızca kendi byte aralığını kapsadığı için doğrulamadan geçmeye devam eder. Denetçinin gerçekten sorduğu sorular ise hangi alanın hangi imzadan sonra değiştiği ve bu değişikliğe izin verilip verilmediğidir

DocMDP tek başına düzenlenmiş kilitli bir alanı neden yakalayamaz?

DocMDP bunu yakalayamaz çünkü DocMDP değişiklik türlerini sınıflandırır, tek tek alanları değil. ISO 32000-1 §12.8.2.2 bölümündeki sertifikasyon izin düzeyleri, sonraki güncellemelerin formları doldurup imzalayabileceğini (P 2) yoksa ayrıca açıklama da ekleyebileceğini (P 3) söyler ve bu izin belgedeki her alana eşit uygulanır. Order.Total alanını düzenlemek form doldurmaktır; dolayısıyla P 2 sertifikasyonu, talep edenin yasaklamak istediği düzenlemeyle tam olarak tatmin olur. PDFlibPas'ta belge düzeyinde değişiklik sınıflandırması o katmanı kapsar, bu makale ise bir adım daha inceye iner

FieldMDP alan ayrıntı düzeyindeki katmandır. Bir imza alanı /Lock sözlüğü taşıyabilir (ISO 32000-1 §12.7.4.5); taşıdığı imza da imza referansında bir FieldMDP dönüşümü barındırabilir (ISO 32000-1 §12.8.2.4). İkisi aynı sözcük dağarcığını kullanır: /Action /All her alanı kilitler, /Action /Include /Fields içinde listelenen adları kilitler, /Action /Exclude ise /Fields içindekiler dışındaki her şeyi kilitler. v3.586.0 öncesinde GetSignatureModificationVerdict, sonraki bir revizyonun bir form alanını değiştirdiğini biliyor ama hangisi olduğunu bilmiyordu; bu yüzden alan kilidi taşıyan ve sonraki güncellemeleri doğrulama malzemesinin ötesine geçen her belge 4, yani belirsiz olarak dönüyordu. Revizyon haritası artık sınıflandırdığı her alanla ilgili nesne için çözülmüş bir alan adı kaydediyor; o 4'ü gerçek bir 2 ya da 3'e çeviren de tam olarak bu

Her onaycı imzalarken alan kilidini yazmak

TPDFlib.SetSignProcessFieldLock, bir imzalama sürecinin üreteceği sonraki imza için kilidi ayarlar. Action 1 All, 2 Include, 3 Exclude demektir, 0 ise kilidi temizler; Include ile Exclude, noktalı virgülle ayrılmış tam nitelikli alan adı listesi alır. İmzalanan alan bir /Lock sözlüğü ve onunla eşleşen bir FieldMDP dönüşümüyle sonuçlanır; katı doğrulayıcıların beklediği eşleşme budur

PDFlibPas SetSignProcessFieldLock action kodları, Delphi FieldMDP imzalaması: action 1 All, sonraki imza alanları dahil her form alanını kilitler; action 2 Include adı geçen veri alanlarını kilitler; action 3 Exclude listedekiler dışındaki her şeyi kilitler; action 0 kilidi temizler
Sahip olunan veri alanları için Include seçin ya da sonraki imza alanlarını listeleyerek Exclude kullanın; reddedilen çağrı 0 döndürür ve önceki kilidi yerinde bırakır, o yüzden dönüş değerini daima kontrol edin
var
  Pdf: TPDFlib;
  SignId: Integer;
begin
  Pdf := TPDFlib.Create;
  try
    SignId := Pdf.NewSignProcessFromFile('purchase-request.pdf', '');
    if SignId = 0 then
      raise Exception.Create('cannot open the form');
    try
      Pdf.SetSignProcessField(SignId, 'Approval.Requester');
      Pdf.SetSignProcessPFXFromFile(SignId, 'requester.pfx', PfxPassword);
      // 2 = Include: bu imza tarafından dondurulan alanlar yalnızca bunlar
      if Pdf.SetSignProcessFieldLock(SignId, 2,
        'Order.Customer;Order.Total') <> 1 then
        raise Exception.Create('field lock rejected');
      Pdf.EndSignProcessToFile(SignId, 'purchase-request-signed.pdf');
      if Pdf.GetSignProcessResult(SignId) <> 1 then
        raise Exception.CreateFmt('signing failed, code %d',
          [Pdf.GetSignProcessResult(SignId)]);
    finally
      Pdf.ReleaseSignProcess(SignId);
    end;
  finally
    Pdf.Free;
  end;
end;

SetSignProcessFieldLock, ad verilmediğinde Include ya da Exclude çağrısı için 0 döndürür; 'Order.Customer;;Order.Total' gibi boş bir girdi barındıran liste de aynı sonucu alır. Reddedilen çağrı önceden yapılandırılmış kilidi yerinde bırakır; o yüzden son çağrının kazandığını varsaymak yerine dönüş değerini kontrol edin. İmzalama kurulumunun geri kalanı — özet (digest) seçimi, /Contents rezervasyonu ve sonuç kodları — PDFlibPas için PAdES imzalama ve doğrulama rehberinde ele alınır. Onay zincirlerine özgü bir tasarım noktası: talep eden imzasındaki bir /All kilidi Approval.Manager alanını da dondurur, çünkü imza alanı da bir form alanıdır. Onaycının sahip olduğu veri alanları için Include kullanın ya da sonraki imza alanlarını listeleyerek Exclude kullanın

Kararı bir onay zinciri boyunca çalıştırmak

GetSignatureModificationVerdict bir seferde tek bir imzayı yargılar; dolayısıyla bir onay zinciri, imza alanı başına bir çağrı yapılarak kontrol edilir. Dönüş değeri küçük ve kapalı bir kümedir ve her değer, inceleyen kişi için farklı bir şey ifade eder:

  • 0: dosya ya da adı geçen imza alanı okunamadı
  • 1: imzadan sonra hiçbir şey eklenmemiş
  • 2: yalnızca etkin izinlerin izin verdiği değişiklikler eklenmiş ve kilitli hiçbir alana dokunulmamış
  • 3: sonraki bir revizyon izin verilmeyen bir değişiklik yapmış; FieldMDP'nin kilitlediği bir alanın düzenlenmesi de buna dahil
  • 4: yanıt belirlenemedi; bu kesinlikle onay olarak okunmamalı
  • 5: imza bir revizyon sınırında bitmiyor; byte'lar kapsadığını iddia ettiği aralığın içinde duruyor
const
  Approvals: array[0..2] of WideString =
    ('Approval.Requester', 'Approval.Manager', 'Approval.Finance');
var
  Pdf: TPDFlib;
  I, Verdict: Integer;
begin
  Pdf := TPDFlib.Create;
  try
    for I := Low(Approvals) to High(Approvals) do
    begin
      Verdict := Pdf.GetSignatureModificationVerdict(
        'purchase-request-final.pdf', FormPassword, Approvals[I]);
      // iki getter da az önce yapılan çağrıyı anlatır; sonraki çağrıdan önce okuyun
      Log.Add(Format('%s: verdict %d, MDP level %d, %s',
        [Approvals[I], Verdict, Pdf.GetSignatureEffectiveMDPLevel,
         Pdf.GetSignatureModificationDetail]));
      case Verdict of
        1, 2: ;                                 // kabul edildi
        3: RejectForm(Approvals[I]);            // detail, alanı ve revizyonu adıyla verir
      else
        QueueForManualReview(Approvals[I]);     // 0, 4, 5
      end;
    end;
  finally
    Pdf.Free;
  end;
end;

Kurcalanmış satın alma talebini ele alın: revizyon 0 boş form, revizyon 1 ile 2 iki imza, revizyon 3 ise düzenlemedir. Kilit tanımları imzalanmış her alandan havuzlandığı için Approval.Manager kararı zaten talep edenin kilidini uygular ve onun sonraki tek güncellemesi düzenlemenin kendisidir. O güncelleme yalnızca alan sözlüğünü yeniden yazdığında karar 3 olur ve detay Revision 3 changed locked form field "Order.Total", which FieldMDP does not allow. iletisini verir. Tek satır her iki denetim sorusunu da yanıtlar. Approval.Requester kararı önce yöneticinin imzaladığı revizyonu geçmek zorundadır ve aşağıdaki sınırlamalar bölümündeki nedenlerle çoğu zaman 4 raporlar; en net yanıtı genellikle en son imza verir. GetSignatureEffectiveMDPLevel, imzalar arasında bulunan en düşük DocMDP P değerini raporlar; belgeyi hiçbir şey sertifikalamamışsa 3 verir ve her iki getter da yalnızca bir sonraki karar çağrısına kadar geçerlidir

PDFlibPas GetSignatureModificationVerdict tarafından okunan kurcalanmış bir Delphi PDF formunun artımlı güncelleme zaman çizelgesi: revizyon 1, Order.Customer ve Order.Total üzerinde Include kilidi taşır; revizyon 2 Approval.Manager alanını imzalar; kilitli Order.Total alanının revizyon 3 yeniden yazımı, yönetici imzası için 3 talep eden imzası için 4 kararı üretir
Kilitler imzalanmış her alandan havuzlanır; Approval.Manager kararı talep edenin kilidini zaten uygular, detay dizisi suçlu alanı ve revizyonu adıyla verir, en eski imza ise çoğu zaman 4'e düşer

PDFlibPas bir revizyonun hangi alanı değiştirdiğini nasıl bilir?

PDFlibPas alan adını her revizyonun nesne grafiğinden yeniden kurar, çünkü değişen bir nesne tam adını nadiren taşır. /FT ya da /V barındıran bir alan sözlüğü ya da bir widget açıklaması, kısmi adını yalnızca /T içinde tutar. ISO 32000-1 §12.7.3.2 bölümünde tanımlanan tam nitelikli ad, kökten itibaren noktalarla birleştirilen kısmi ad zinciridir; revizyon haritası bu yüzden /Parent bağlarını yukarı doğru gezinir ve bulduğu her /T'yi öne ekler. Her ebeveyn, sınıflandırılan revizyondaki ya da ondan önceki en yeni tanımından okunur; böylece bir ebeveyn düğümün sonradan adının değiştirilmesi, önceki düzenlemelerin geçmişini yeniden yazamaz

PDFlibPas'ta Delphi için FieldMDP tam nitelikli ad çözümü: revizyon haritası değişen widget'tan yukarı doğru /Parent bağlarını gezinir, Order.Total'ı kurmak için noktalarla birleştirilmiş her /T kısmi adını öne ekler ve bunu /Fields girdileriyle tam eşitlik ya da nokta önekiyle eşleştirir; böylece Order üzerindeki bir kilit tüm alt alanları kapsar
Değişen bir nesne tam adını nadiren taşır: ebeveynler sınıflandırılan revizyondaki ya da öncesindeki en yeni tanımlarından okunur, adlar büyük/küçük harfe duyarlı karşılaştırılır ve gezinme sekiz düzeyden sonra durur

Çözülen adı kilitlerle eşleştirmek bilinçli olarak basittir. Bir ad, bir /Fields girdisiyle tam eşit olduğunda ya da girdiyle nokta eklenerek başladığında o girdiyle eşleşir; dolayısıyla Order alanını kilitlemek Order.Total ve öteki tüm alt alanları da kilitler. All her adı kilitler, Include eşleşen adları kilitler, Exclude eşleşmeyen her adı kilitler. Kilit tanımları dosyadaki imzalanmış her imza alanından gelir; bir alan hem /Lock sözlüğü hem FieldMDP dönüşümü barındırıyorsa /Lock değerleri kullanılır. Her imzanın tam olarak neyi kilitlediğini görmek için, daha alt düzeydeki PDFlibDigitalSign ünitesindeki TPDFlibSignDoc sınıfı, kararın okuduğu izin raporunu açar:

uses
  PDFlibDigitalSign;

var
  SignDoc: TPDFlibSignDoc;
  Report: TPDFlibSignaturePermissionReport;
  Names: TStringList;
  I, J: Integer;
  Line: string;
begin
  SignDoc := TPDFlibSignDoc.Create;
  Names := TStringList.Create;
  try
    if not SignDoc.Open('purchase-request-final.pdf', FormPassword, False) then
      Exit;
    try
      SignDoc.GetSignatureFieldNames(Names);
      for I := 0 to Names.Count - 1 do
      begin
        SignDoc.GetSignaturePermissionInfoByName(Names[I], Report);
        if not (Report.HasFieldLock or Report.HasFieldMDP) then
          Continue;
        Line := Names[I] + ': ' + string(Report.FieldLockAction);
        for J := 0 to High(Report.FieldNames) do
          Line := Line + ' ' + string(Report.FieldNames[J]);
        if Report.HasFieldLock and Report.HasFieldMDP and
          not Report.FieldMDPMatches then
          Line := Line + ' (lock and FieldMDP transform disagree)';
        Log.Add(Line);
      end;
    finally
      SignDoc.Close;
    end;
  finally
    Names.Free;
    SignDoc.Free;
  end;
end;

Şifreli formlar: parolayı iletin ya da adlar okunamaz

Şifreli bir formda herhangi bir alan adı çözülebilmesi için önce şifrenin çözülmesi gerekir; dolayısıyla GetSignatureModificationVerdict, Password argümanına yalnızca dosyayı açmak için değil daha fazlası için ihtiyaç duyar. /T gibi dizeler nesne başına şifrelenir ve sıkıştırılmış nesne akışlarına yazılmış alanlar, kapsayan akışın şifresi çözülüp açılıncaya dek hiç okunamaz. v3.586.0'dan bu yana dahili revizyon haritası parolayı kabul eder, nesne akışlarını ve revizyon sözlüklerini ayrıştırırken bir şifre çözme bağlamı ekler ve kodunu çözdüğü her nesnenin şifresini açar; buna karşılaştırma yaptığı önceki sürümler ile gezindiği ebeveynler de dahildir

Revizyon haritasındaki nesne başına şifre çözme, yalnızca Password boş olmadığında devreye girer. Bu yüzden şifreli bir dosya üzerinde boş parolayla hesaplanan bir karar, belge kendisi sormadan açılmış olsa bile doğrulanmamış sayılmalıdır. Form parolasını iş akışı kaydının yanında saklayın ve her çağrıda iletin

FieldMDP kararının kapsamadıkları

Alan kontrolü dar tek bir soruya verilen yapısal bir yanıttır ve birkaç durum onun dışında kalır. Bunları önceden bilmek, bir 4'ün destek tırmandırmasına dönüşmesini önleyen şeydir:

  • Alan karşılaştırması yalnızca belge düzeyindeki kontrol temiz çıktıktan ve sonraki en az bir revizyon, doğrulama malzemesi ya da zaman damgalarının ötesinde bir şey değiştirdikten sonra çalışır; DocMDP ihlali, hiçbir alana bakılmadan 3 olarak rapor edilir
  • İmza değeri sözlüğü, yeniden yazılmış bir AcroForm sözlüğü ya da /AcroForm veya /Perms ile sınırlı bir katalog değişikliği gibi imzayla ilgili sınıflandırılan ama alan adı taşımayan nesneler, kararın tahmin etmek yerine 4 döndürmesine neden olur; etkin bir kilit altında sonraki bir onay imzası ekleyen revizyon bu yüzden çoğu zaman 4'e düşer
  • Yalnızca alan sözlükleri ve widget açıklamaları alan adı alır; revizyon haritasının görünüm akışları (appearance stream) için ayrılmış bir sınıfı yoktur, bu yüzden değille birlikte görünümleri de yeniden üreten bir düzenleyici, alan kontrolü çalışmadan belge düzeyindeki kontrol tarafından içerik değişikliği olarak durdurulabilir; detay dizisi bu iki durumu birbirinden ayırır
  • Tarama, karşılaştığı ilk kilitli alanda ya da adsız girdide durur; bir 3, suçlu alanlardan yalnızca birini adlandırır, hepsini değil
  • Kilitler, zaman sırasına sokulmadan imzalanmış her imza alanından havuzlanır; bu yüzden sonradan imzalayan birinin yazdığı kilit, kendinden önceki güncellemelere de uygulanır. Bu, bir sonucu yalnızca 3'e doğru itebilir, asla 2'ye doğru değil
  • Adlar tam ve büyük/küçük harfe duyarlı karşılaştırılır ve /Parent gezinmesi sekiz düzeyden sonra durur; alışılmadık ölçüde derin bir hiyerarşi bu yüzden kırpılmış bir ada çözülür
  • Yapısı doğrulamayı geçmeyen ve eylemi okunamayan bir FieldMDP girdisi, kararın 4 döndürmesine neden olur

Bir karar 4 olarak döndüğünde GetSignatureRevisionIndex, imzanın bittiği revizyonu verir; GetDocumentRevisionChanges ise her sonraki revizyonun eklediği, değiştirdiği ya da serbest bıraktığı her nesneyi değişiklik sınıfıyla birlikte listeler. Bir kişinin bir dakikada karar vermesi için bu yeter. Söz konusu revizyonların nasıl eklendiğinin mekaniği PDFlibPas için artımlı güncelleme rehberinde ele alınır

SSS

2 kararı imzaların geçerli olduğu anlamına gelir mi?

Hayır. GetSignatureModificationVerdict, bir imzadan sonra ne eklendiğini inceler ve CMS imza değerine, sertifika zincirine ya da iptal (revocation) bilgisine hiç bakmaz. 2, sonraki güncellemelerin izinler içinde kaldığını söyler; kriptografik doğrulama ayrı bir adımdır ve ikisi de gerekir

İkinci onaycının imzalaması neden 2 yerine 4 üretir?

İmzalama, bir imza değeri sözlüğü ekler ve genellikle AcroForm sözlüğü gibi form defter tutmasını yeniden yazar. O nesneler adı geçen hiçbir alana ait değildir ve alan kilidi etkinken karar, bir alana atfedemediği değişikliği temizlemeyi reddeder. Revizyonun yalnızca beklenen imza nesnelerini içerdiğini teyit etmek için GetDocumentRevisionChanges kullanın

Tek bir ad, bir alan grubunun tamamını kilitleyebilir mi?

Evet. Bir kilit girdisi, adı geçen alanla ve tam nitelikli adı onunla nokta eklenerek başlayan her alt alanla eşleşir; dolayısıyla bir Include kilidinde Order listelemek, Order.Customer, Order.Total ve o ebeveynin altındaki öteki tüm alanları dondurur

FieldMDP kararları, alan kilidi imzalaması ve revizyon incelemesinin tamamı PDFlibPas Delphi PDF Library ile gelir; yukarıdaki onay iş akışının dayandığı DocMDP sertifikasyonu ve PAdES uzun vadeli doğrulama özellikleri de aynı pakette