Teknik Makale

PDF Library for Delphi: PAdES signing and validation in Delphi

Tek bir PAdES imzasını doğrulamak, üç bağımsız şeyi kontrol etmek anlamına gelir ve bir görüntüleyicideki yeşil onay işareti size yalnızca üçüncüsü hakkında bir şey söyler. Birincisi, /ByteRange dizisi doğru baytları kapsamak zorundadır: adlandırdığı aralıklar, imzalı hiçbir bayt dışarıda kalmadan, CMS özetinin alındığı tam girdiyi yeniden kurmalıdır. İkincisi, CMS'nin içindeki sertifika, güvendiğiniz bir köke zincirlenmek ve PAdES'in gerektirdiği imzalı imzalama-sertifikası özniteliğini taşımak zorundadır. Üçüncüsü, profil bir zaman damgası iddia ediyorsa, bir RFC 3161 tokeni, sertifika süresi dolmadan önceki bir zaman noktasına imza değerini bağlamak zorundadır. Acrobat, üçünü de tek bir simgeye sıkıştırır; bir uygunluk denetleyicisi bunları ayrı tutar ve bu dosyaları üreten kod da öyle yapmalıdır. losLab PDF Library (PDF Library for Delphi), bunun imzalama tarafını, zaman damgası yeniden gömme işlemini ve güvenmeden önce bir ByteRange'i incelemenizi sağlayan denetim çağrılarını size verir

Bir ayrım, neredeyse her ilk PAdES uygulamasını tökezletir, bu yüzden herhangi bir koddan önce belirtmeye değer. /SubFilter /adbe.pkcs7.detached ile yazılan bir imza, Acrobat'ın geçerli olarak bildireceği tamamen sağlam bir ISO 32000-1 §12.8 imzasıdır. Ama bir PAdES imzası değildir, çünkü ETSI EN 319 142-1 her temel düzeyde ETSI.CAdES.detached'i gerektirir. Bir eIDAS uygunluk denetleyicisi, kriptografi aynı olsa bile birincisini reddeder ve ikincisini kabul eder. Profil, belgenin kendisi hakkında yaptığı bir iddiadır ve o iddiayı doğru yapmak PDF Library for Delphi'ta tek bir çağrıdır

Bir PDF imzasını PAdES imzasına dönüştüren nedir

ETSI EN 319 142-1, CMS biçimi üzerine yığılmış dört temel düzey tanımlar. PAdES-B-B giriş noktasıdır: ETSI.CAdES.detached SubFilter'ı ve imzalı bir imzalama-sertifikası özniteliği taşıyan, bir PDF imza alanındaki bir CAdES imzası. PAdES-B-T, imza değerinin üzerine bir RFC 3161 zaman damgası ekler ve imzanın, kimsenin geri tarihlendiremeyeceği bir zaman noktasından önce var olduğunu kanıtlar. PAdES-B-LT, doğrulama için gereken sertifikaları, CRL'leri ve OCSP yanıtlarını bir Belge Güvenlik Deposu'na gömer; böylece dosya, veren CA altyapısını emekliye ayırdıktan sonra bile doğrulanabilir kalır. PAdES-B-LTA, algoritmalar zayıfladıkça birikmiş kanıtı yeniden koruyan bir belge zaman damgasıyla yığını tepeler

PDF Library for Delphi, bu kavramları kendi imzalama-süreci API'sine eşler. Profil işaretleyicisi SetSignProcessCustomSubFilter'dır. Politikanız bir taahhüt-türü göstergesine ihtiyaç duyuyorsa (köken kanıtı, onay kanıtı veya 1'den 6'ya kadar numaralandırılmış diğer ETSI tanımlayıcılarından biri), bu SetSignProcessCommitmentType üzerinden geçer. Açık bir imza politikası, politika OID'sini ve özetini alan SetSignProcessSignaturePolicy ile eklenir. Bir varsayılan dikkati hak eder: özet algoritması otomatikte bırakıldığında, kütüphane ETSI ve adbe.pkcs7.detached imzaları için SHA-256'yı seçer ve yalnızca eski adbe.pkcs7.sha1 yolunda SHA-1'e düşer. Yine de onu açıkça ayarlayın. Denetçiler hangi özeti kullandığınızı sorar ve koddaki açık bir değer, açıklamak için kılavuzu okumaya gitmeniz gereken bir varsayılandan savunması daha kolaydır

PDF Library for Delphi ile oluşturulan PAdES taban düzeyleri B-B, B-T, B-LT ve B-LTA merdiveni; her düzeyin ETSI.CAdES.detached çekirdeğinin üzerine zaman damgaları, DSS kanıtı veya yenilenebilir bir belge zaman damgası nasıl eklediğini gösterir
Her ETSI baseline düzeyi aynı CAdES çekirdeğine bir güvence daha ekler; imzalı özniteliklerden yenilenebilir belge zaman damgasına kadar

Temel düzey imzayı üretmek

Düz API, imzalamayı tek seferlik bir durum makinesi olarak sürer: kaynak dosya üzerinde bir süreç açın, onu yapılandırın, bir çıktı dosyasına bitirin, sonuç kodunu okuyun. Aşağıdaki dizi, SHA-256 ile bir PAdES-B-B imzası üretir. En çok önem taşıyan satırın imzanın kendisiyle hiçbir ilgisi yoktur. O, bilinçli olarak abartılı büyütülmüş /Contents ayırmasıdır, çünkü bu imzaya bir gün bir zaman damgası eklemeniz gerekirse sonradan değiştiremeyeceğiniz tek şeydir

var
  Pdf: TPDFlib;
  SignId: Integer;
begin
  Pdf := TPDFlib.Create;
  try
    SignId := Pdf.NewSignProcessFromFile('invoice.pdf', '');
    if SignId = 0 then
      raise Exception.Create('cannot open source PDF');
    Pdf.SetSignProcessField(SignId, 'Sig1');
    Pdf.SetSignProcessPFXFromFile(SignId, 'company.pfx', PfxPassword);
    Pdf.SetSignProcessInfo(SignId, 'Approved', 'Vienna', 'billing@example.com');
    Pdf.SetSignProcessCustomSubFilter(SignId, 'ETSI.CAdES.detached');
    Pdf.SetSignProcessDigestAlgorithm(SignId, 2);          // SHA-256
    Pdf.SetSignProcessReserveContentsBytes(SignId, 8192);  // daha sonra bir zaman damgası için yer
    Pdf.EndSignProcessToFile(SignId, 'invoice-signed.pdf');
    if Pdf.GetSignProcessResult(SignId) <> 1 then
      raise Exception.CreateFmt('signing failed, code %d',
        [Pdf.GetSignProcessResult(SignId)]);
    Pdf.ReleaseSignProcess(SignId);
  finally
    Pdf.Free;
  end;
end;

NewSignProcessFromFile, kaynak hiç açılamadığında 0 döndürür. Bundan sonra, GetSignProcessResult, üretimde gerçekten oluşan başarısızlık modlarını ayırır: 4 yanlış bir PDF parolasını, 7 yanlış bir PFX parolasını, 9 özel anahtarı olmayan bir sertifika dosyasını, 10 yazılamayan bir çıktı yolunu, 11 imza baytları uygulanırken oluşan bir hatayı gösterir. Sayısal kodu girdi dosya adının yanına günlüklemek, belirsiz bir destek talebini bir dakikalık bir teşhise dönüştürür

Kütüphanenin sizin için getirmeyeceği RFC 3161 zaman damgasını eklemek

PDF Library for Delphi hiçbir TSA istemcisi göndermez ve bu bir eksiklik değil, bilinçli bir sınırdır. Kütüphane, zaman damgası otoritesinin karşı imzalaması gereken özeti hesaplar ve sonrasında genişletilmiş CMS'yi yeniden gömer; aradaki HTTP alışverişi ve CMS ameliyatı çağırana aittir. Bu ayrımın sıkı bir teknik nedeni vardır. İmzasız öznitelikleri sözde ekleyen Windows CryptoAPI kontrolü CMSG_CTRL_ADD_SIGNER_UNAUTH_ATTR, PAdES'in kullandığı ayrık SignedData düzeninde CRYPT_E_INVALID_INDEX ile başarısız olur. Bu yüzden geliştirilmiş CMS'nin kendi kontrolünüzdeki bir CMS kodlayıcısından gelmesi gerekir. Hiçbir kütüphane, tokeni tek bir sistem çağrısıyla sessizce içine katlayamaz ve öyle iddia eden herhangi biri, ameliyatı sizin göremediğiniz bir yerde yapıyordur

Delphi'de bir PAdES imzasına RFC 3161 zaman damgası ekleme hattı; PDF Library for Delphi'nin karma oluşturma ve gömmesini, çağıranın TSA isteğinden ve ayrılmış /Contents alanı içindeki CMS yeniden kodlamasından ayırır
Kitaplık karmalar ve yeniden gömme yaparken sizin kodunuz jetonu çeker ve CMS operasyonunu gerçekleştirir; sonuç 8192 baytlık /Contents rezervinin içine yerleşmelidir
var
  Pdf: TPDFlib;
  StsId: Integer;
  HashHex, TstDer, TsAttr, AugmentedCms: AnsiString;
begin
  Pdf := TPDFlib.Create;
  try
    StsId := Pdf.NewPAdESSignatureTimeStampProcessFromFile('invoice-signed.pdf', '');
    Pdf.SetPAdESSignatureTimeStampField(StsId, 'Sig1');
    Pdf.SetPAdESSignatureTimeStampDigestAlgorithm(StsId, 2);
    HashHex := Pdf.GetPAdESSignatureValueHashHex(StsId);
    // aşağıdaki iki çağrı da uygulama kodudur: TSA'nıza bir HTTP POST,
    // ve tokeni imzasız bir öznitelik olarak ekleyen bir CMS yeniden kodlaması
    TstDer := RequestTimeStampToken(HashHex);
    TsAttr := Pdf.BuildPAdESSignatureTimeStampAttribute(TstDer);
    AugmentedCms := AttachUnsignedAttribute(Pdf.GetPAdESSignatureCMSBytes(StsId), TsAttr);
    Pdf.SetPAdESSignatureCMSBytes(StsId, AugmentedCms);
    Pdf.EndPAdESSignatureTimeStampProcessToFile(StsId, 'invoice-bt.pdf');
    if Pdf.GetPAdESSignatureTimeStampProcessResult(StsId) <> 1 then
      raise Exception.Create('timestamp embedding failed');
    Pdf.ReleasePAdESSignatureTimeStampProcess(StsId);
  finally
    Pdf.Free;
  end;
end;

Buradaki sonuç kodlarına dikkat edin: 12, adlandırılan imza alanının var olmadığını, 11 mevcut CMS'nin ayrıştırılamadığını ve 13 genişletilmiş CMS'nin artık ayrılmış /Contents yer tutucusuna sığmadığını gösterir. Acı veren kod 13'tür, çünkü tek çözüm yeniden imzalamaktır: sertifika zinciriyle birlikte tipik bir zaman damgası tokeni 4 ila 6 KB tutar ve B-B adımı sırasında yapılan 8192 baytlık ayırma, tam olarak bu adımın inecek yeri olsun diye vardır

Doğrulama, sertifika zincirinde değil ByteRange'de başlar

Bir görüntüleyicideki yeşil onay işareti, dosya hakkında yapısal bir hüküm değil, o makinenin sertifika deposuna karşı bir güven kararıdır. Programlı doğrulama daha aşağıdan, artımlı güncellemelerin incelttiği soruyla başlamalıdır: her imza gerçekte hangi baytları kapsıyor? Burada tartışılan her geliştirme, ister ikinci bir imza, ister bir DSS sözlüğü, ister bir belge zaman damgası olsun, artımlı güncelleme üzerinden gelir ve her güncelleme, önceki imzanın /ByteRange'inin dışında baytlar ekler. Bu eklenen baytlar meşrudur. Bir doğrulayıcı yine de bunları belgenin değişiklik politikasına karşı sınıflandırmak zorundadır ve o politikanın yaşadığı alan başına DocMDP düzeyi GetSignatureDocMDPLevelByName ile okunabilir

Delphi'de imzalı bir PDF'nin bayt düzeni denetimi: ByteRange'in kapsadığı aralıkları, hariç tutulan /Contents baytlarını, aralığın dışındaki eklenmiş artımlı güncellemeleri ve dosya boyutuna karşı kapsam kararını gösterir
İmzanın kendi baytları dışlandığı iki kapsanan aralık gerçek kapsamı anlatır; eklenen güncellemeler DocMDP politikasına göre sınıflandırılır, korkulmaz
var
  Doc: TPDFlibSignDoc;
  Names: TStringList;
  I: Integer;
  B0, B1, B2, B3, FileSize: Int64;
begin
  FileSize := TFile.GetSize('invoice-bt.pdf');  // Open'dan önce: SignDoc bir paylaşım kilidi tutar
  Doc := TPDFlibSignDoc.Create;
  try
    if not Doc.Open('invoice-bt.pdf', '', False) then
      raise Exception.Create('cannot open for audit');
    Names := TStringList.Create;
    try
      Doc.GetSignatureFieldNames(Names);
      for I := 0 to Names.Count - 1 do
        if Doc.GetSignatureValueObjNum(Names[I]) > 0 then   // >0 gerçekten imzalandığı anlamına gelir
        begin
          B0 := StrToInt64(string(Doc.GetSignatureValueByName(Names[I], 11)));
          B1 := StrToInt64(string(Doc.GetSignatureValueByName(Names[I], 12)));
          B2 := StrToInt64(string(Doc.GetSignatureValueByName(Names[I], 13)));
          B3 := StrToInt64(string(Doc.GetSignatureValueByName(Names[I], 14)));
          if (B0 = 0) and (B2 + B3 = FileSize) then
            Writeln(Names[I], ': covers the file to EOF')
          else
            Writeln(Names[I], ': earlier revision, or unexpected ByteRange layout');
        end;
    finally
      Names.Free;
    end;
    Doc.Close;
  finally
    Doc.Free;
  end;
end;

Bu denetim yolunda iki tuzak yaşar. TPDFlibSignDoc.Open, dosyayı özel bir paylaşım kilidiyle tutar; bu yüzden CMS doğrulaması için ham dosya baytlarını da özetlemek isteyen bir doğrulayıcı, dosyayı denetim için açmadan önce belleğe okumak zorundadır. Bu sırayı tersine çevirirseniz okuma, kendi koyduğunuz bir kilitte başarısız olur. İkinci tuzak yüksek sesli değil sessizdir: düz-API karşılığı GetSignProcessByteRange, alttaki ofsetler Int64 iken bir Integer döndürür; bu yüzden 2 GB'ın ötesinde düz çağrı şikayet etmeden kırpar, bu örneğin ofsetleri bunun yerine denetim sınıfı üzerinden çekmesinin nedeni de budur. Adlandırılmaya değer bir eksiklik daha var. Düz katmanın hiçbir VerifySignature sarmalayıcısı yoktur. Kriptografik hükümler, vsValid, vsInvalid veya vsUnknown döndüren sınıf düzeyindeki TPDFlibSignatureVerifier'dan veya uyumluluk politikanızın zaten güvendiği harici bir doğrulayıcıdan gelir

Uzun vadeli doğrulama: DSS, VRI ve belge zaman damgası

PAdES-B-LT, geçersiz kılma altyapısı ölümlü olduğu için vardır. ETSI EN 319 142-1 §5.4.2.2, Belge Güvenlik Deposu'nu belirtir: sertifikaları, CRL'leri ve OCSP yanıtlarını taşıyan, isteğe bağlı olarak her imzanın /Contents'inin özetiyle anahtarlanmış VRI girdileri üzerinden imza başına indekslenen, belge düzeyinde bir sözlük. PDF Library for Delphi akışı, zaman damgası tasarımını yansıtır. NewPAdESDSSProcessFromFile süreci açar; AddPAdESDSSCertificate, AddPAdESDSSCRL ve AddPAdESDSSOCSP DER blob'ları kabul eder; AddPAdESDSSVRI seçili malzemeyi bir imzaya bağlar; EndPAdESDSSProcessToFile her şeyi bir artımlı güncelleme olarak yazar. Zor kısım sizin tarafınızda kalır. Geçersiz kılma malzemesini getirmek ve onun gömmeye değecek kadar taze olup olmadığına karar vermek, çağıranın işidir. Kütüphane, sözlüklerin yapısal olarak uygun olduğunu garanti eder; OCSP yanıtlayıcınızın doğruyu söylediğini garanti edemez

Arşivsel uç nokta B-LTA, bir belge zaman damgası ekler: ayrılmış bir imza uzunluğuyla SetSignProcessDocTimeStamp üzerinden üretilen, türü Sig değil DocTimeStamp olan ayrı bir imza alanı. Bu, B-T adımındaki imza zaman damgasının yerini almaz. İmza zaman damgası, belirli bir imzanın ne zaman var olduğunu kanıtlar; belge zaman damgası ise DSS kanıtı dahil olmak üzere tüm dosyayı korur ve algoritmalar zayıfladıkça uzun vadeli bir arşivin birkaç yılda bir yenilediği unsurdur. Olgun bir arşivsel profil ikisini de taşır. Bu yapılardan öncesine ait okuyucular için TPDFlibSignDoc.EnsurePAdESExtensions, dosyanın ETSI tanımlı özellikler kullandığını duyurarak ESIC geliştirici uzantısını belge kataloğuna kaydeder

Tüm bunlara bir tepkinin önüne geçmeye değer, çünkü bir hata gibi görünür ama değildir. Bir görüntüleyici, PAdES yapısı tamamen doğru olan bir dosyada sık sık "geçerlilik bilinmiyor" bildirir. Güven ve yapı bağımsız eksenlerdir. Görüntüleyici, ByteRange denetimi ve CMS doğrulaması ikisi de geçse bile, imzalayanı o makinede güvendiği bir köke zincirleyemez; bu, özel CA'lar ve test sertifikalarıyla rutindir. Çözüm, kök sertifikayı düzgün dağıtmak veya gerçek hedef nitelikli eIDAS statüsüyse AB güvenilir listelerine karşı değerlendirmektir, imzalama koduna dokunmak değil

Denetim tarafı perspektifi için, yani bir belge kümesi genelinde imza alanlarını numaralandırmak, ByteRange düzenlerini dökmek ve DocMDP düzeylerini toplu okumak için, uyumluluk ve imzalama çalışma tezgâhı konulu yoldaş yazıya bakın. Aynı zamanda arşivsel politikayı da karşılamak zorunda olan imzalı belgeler, Delphi'de PDF/A ve PDF/UA ön kontrolü makalesinde açıklanan iş akışına aittir. Tam API belgeleri ve değerlendirme indirmeleri, losLab PDF Library for Delphi ürün sayfasındadır