Artikel Teknis

Verifikasi Tanda Tangan Digital PDF di Delphi dengan HotPDF

HotPDF memverifikasi tanda tangan digital dalam dokumen PDF yang dimuat melalui tiga metode THotPDF: GetLoadedSignatureInfo, VerifyLoadedSignature, and VerifyLoadedSignatureEx, yang diperkenalkan di v2.259.0. Komponen ini menghitung ulang hash segmen /ByteRange dari file asli, memeriksa atribut CMS messageDigest, dan menjalankan verifikasi RSA PKCS#1 v1.5 terhadap sertifikat penandatangan tersemat, mengembalikan svValid ketika bita dokumen utuh

Skenarionya biasa saja tetapi taruhannya tidak. Pihak lawan mengembalikan kontrak yang telah ditandatangani, alur kerja Anda perlu mengarsipkannya, dan seseorang mengajukan satu-satunya pertanyaan yang penting: apakah ini dokumen yang kita kirim, bita demi bita, ditandatangani oleh sertifikat yang diklaimnya? Menjawab hal itu dalam kode adalah sisi verifikasi dari kisah tanda tangan; sisi penandatanganan, pembuatan, dan penyematan tanda tangan PAdES sejak awal, dibahas dalam artikel pendamping tentang pembuatan tanda tangan digital PAdES dengan HotPDF. Artikel ini membahas arah sebaliknya: PDF tiba dalam keadaan sudah ditandatangani, dan Anda menginginkan keputusan terprogram daripada sekadar tangkapan layar tanda centang hijau Acrobat

Bagaimana PDF bertanda tangan membuktikan bahwa ia tidak dirusak?

Tanda tangan PDF melindungi rentang bita tertentu dari file, bukan gagasan abstrak tentang "dokumen." ISO 32000-1 §12.8 mendefinisikan mekanisme tersebut: bidang formulir tanda tangan membawa kamus yang entri /Contents-nya menampung wadah CMS SignedData (RFC 5652), dan yang larik /ByteRange-nya menyebutkan wilayah file yang tepat yang dicakup oleh tanda tangan, sesuai §12.8.1. Larik tersebut adalah daftar pasangan offset dan panjang, dalam praktiknya dua segmen: semua yang ada sebelum string heksadesimal /Contents, dan semua yang ada setelahnya. Nilai tanda tangan tidak dapat mencakup dirinya sendiri, sehingga file di-hash di sekitar lubang tersebut

Bagaimana PDF bertanda tangan membuktikan bahwa ia tidak dirusak?

Desain tersebut memiliki konsekuensi yang membentuk seluruh API: verifikasi harus menghitung hash bita terserialisasi yang asli, persis seperti yang ada di disk. Model objek yang diuraikan tidak berguna untuk hal ini, karena menyerialisasi ulang dokumen yang tidak berubah pun menghasilkan bita yang berbeda. Oleh karena itu, HotPDF memverifikasi terhadap file sumber dari mana dokumen dimuat, atau terhadap TStream bita mentah yang Anda sediakan, tidak pernah terhadap representasi dalam memorinya

Membaca metadata tanda tangan sebelum memverifikasi apa pun

GetLoadedSignatureInfo mengurai kamus tanda tangan dan wadah CMS-nya tanpa menyentuh satu bita dokumen pun, yang menjadikannya panggilan pertama yang tepat ketika Anda hanya perlu menampilkan siapa yang menandatangani dan kapan. Bidang tanda tangan diindeks dari 0 dalam urutan bidang formulir, dan GetLoadedSignatureFieldCount memberi tahu Anda berapa banyak yang ada. Rekaman THPDFSignatureInfo yang dikembalikan membawa nama bidang, /SubFilter, nama umum (common name) sertifikat penandatangan, distinguished name subjek dan penerbit, nomor seri, tanggal validitas, waktu penandatanganan (dari atribut bertanda tangan jika ada, jika tidak entri /M kamus), nama algoritma digest, dan string /Reason, /Location, serta /ContactInfo. Anggota Status-nya tetap svNotVerified, label jujur untuk "diuraikan, tidak diperiksa"

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;

Menjalankan pemeriksaan kriptografis

VerifyLoadedSignatureEx melakukan verifikasi penuh untuk dokumen yang dimuat dari file dan menyerahkan kembali rekaman info yang terisi dalam satu panggilan: ia membuka kembali file sumber, menghitung hash segmen /ByteRange dengan algoritma digest SignerInfo, membandingkan hasilnya dengan atribut bertanda tangan messageDigest (RFC 5652 §5.4), dan kemudian memverifikasi RSA tanda tangan di atas pengodean ulang DER SET dari atribut bertanda tangan. Ketika tanda tangan tidak membawa atribut bertanda tangan, pemeriksaan RSA dijalankan secara langsung di atas hash dokumen sebagai gantinya. Tanda tangan yang didukung adalah RSA PKCS#1 v1.5 dengan digest SHA-1, SHA-256, SHA-384, atau SHA-512, yang mencakup subfilter adbe.pkcs7.detached dan ETSI.CAdES.detached yang dihasilkan oleh alat penandatanganan utama

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;

Dua detail implementasi patut diketahui karena menjelaskan kegagalan yang terlihat misterius dari luar. Pertama, pemeriksaan atribut bertanda tangan sangat pemilih tentang pengodean: di dalam file, atribut tersebut ditandai dengan [0] IMPLICIT, tetapi tanda tangan dihitung atas bentuk DER SET OF-nya, sehingga verifikator menandai ulang sebelum menghitung hash, persis seperti yang disyaratkan RFC 5652 §5.4. Verifikator buatan sendiri yang menghitung hash bita seperti yang muncul di file akan menolak setiap dokumen yang ditandatangani dengan benar. Kedua, /Contents secara konvensional diisi nol (zero-padded) ke anggaran bita yang dicadangkan, sehingga verifikator memotong blob DER ke panjang sebenarnya dari SEQUENCE luarnya sebelum mengurai; nol yang membuntuti dan tampak seperti sampah adalah hal yang normal, bukan kerusakan. Keluarga bahaya penguraian ASN.1 yang sama, pada sisi impor sertifikat, adalah subjek dari artikel tentang pengerasan keamanan PKCS#12 dan ASN.1 di HotPDF

Apa yang sebenarnya dijamin oleh tanda tangan yang valid?

svValid berarti tepat hal ini: bita yang dinamai oleh /ByteRange di-hash ke nilai yang ditandatangani oleh penandatangan, dan tanda tangan terverifikasi di bawah kunci publik sertifikat yang tertanam dalam wadah CMS. Itu adalah integritas bita ditambah pengikatan kunci, dan tidak ada yang lain. Rantai sertifikat dan validasi kepercayaan secara eksplisit berada di luar cakupan untuk verifikator HotPDF: ia tidak menelusuri rantai ke root, memeriksa pencabutan, atau berkonsultasi dengan toko kepercayaan (trust store) mana pun. Sertifikat yang ditandatangani sendiri dari penyerang yang menandatangani ulang dokumen yang dimodifikasi akan terverifikasi sebagai svValid, karena matematikanya konsisten secara internal. Apakah penandatangan adalah orang yang mereka klaim, dan apakah ada yang harus mempercayai mereka, is a policy decision that belongs to a separate layer, whether that is your organization's certificate whitelist, the Windows certificate store, or a validation authority

Bendera CoversWholeDocument menjaga celah yang lebih halus. Tanda tangan hanya pernah mencakup /ByteRange miliknya, dan mekanisme pembaruan inkremental PDF memungkinkan penambahan konten setelah tanda tangan tanpa membatalkannya, yang sengaja dirancang dan begitulah cara fungsi alur kerja multi-tanda tangan. Bendera tersebut dihitung selama verifikasi dan bernilai true hanya ketika dua segmen ditambah celah /Contents menjangkau seluruh file. Ketika svValid tiba dengan CoversWholeDocument bernilai false, revisi yang ditandatangani utuh tetapi file berisi penambahan setelahnya, dan apa yang diubah oleh penambahan tersebut adalah sesuatu yang harus diputuskan oleh alur kerja Anda apakah akan ditoleransi

Dokumen yang dimuat dari aliran dan dokumen terenkripsi membutuhkan bita sumbernya sendiri

Panggilan VerifyLoadedSignature dan VerifyLoadedSignatureEx tanpa parameter bergantung pada komponen mengingat file mana asal dokumen tersebut. Jika Anda memuat dokumen dari aliran, tidak ada nama file untuk dibuka kembali; hal yang sama berlaku setelah jalur pemuatan ulang kata sandi yang digunakan untuk dokumen terenkripsi, alur kerja yang dijelaskan dalam artikel tentang enkripsi PDF AES-256 dengan HotPDF. Dalam kedua kasus, kelebihan beban yang didukung file mengembalikan svSourceUnavailable daripada menebak. Perbaikannya adalah kelebihan beban TStream, yang memungkinkan Anda menyerahkan bita mentah asli dari tempat Anda menyimpannya, file yang masih Anda miliki, penyangga memori, atau blob database

var
  Src: TFileStream;
  Status: THPDFSignatureVerifyStatus;
  Info: THPDFSignatureInfo;
begin
  // Dokumen yang dimuat dari aliran: komponen tidak menyimpan nama
  // file sumber, jadi sediakan bita asli Anda sendiri.
  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;

Melaporkan apa yang tidak dapat Anda verifikasi

Verifikator yang hanya mengetahui "valid" dan "tidak valid" akan salah melaporkan dokumen yang sekadar tidak dipahaminya, sehingga enumerasi status memisahkan kasus-kasus yang harus dibedakan oleh UI Anda. svDigestMismatch berarti bita dokumen berubah setelah penandatanganan, sinyal perusakan klasik. svSignatureInvalid berarti bita di-hash dengan benar tetapi pemeriksaan RSA gagal, yang menunjukkan nilai tanda tangan yang rusak atau dipalsukan. svUnsupportedAlgorithm adalah jawaban jujur untuk kunci ECDSA dan digest yang tidak dikenal: tanda tangan tersebut mungkin sangat baik, HotPDF hanya tidak dapat memeriksanya, dan melaporkannya sebagai "tidak valid" akan memfitnah dokumen yang sehat. svMalformed menandai wadah CMS yang tidak dapat diurai sama sekali. Untuk pemeriksaan gaya gerbang, VerifyAllLoadedSignatures mengembalikan true hanya ketika setidaknya satu bidang tanda tangan ada dan setiap bidang tersebut terverifikasi sebagai svValid, sebuah boolean tunggal yang nyaman untuk alur penyerapan arsip yang menolak apa pun yang kurang dari itu

Verifikasi tanda tangan, penandatanganan PAdES, enkripsi AES-256, dan API pengeditan dokumen yang dimuat semuanya dikirimkan dalam pustaka VCL asli yang sama untuk Delphi dan C++Builder, tanpa dependensi DLL eksternal; daftar fitur lengkap dan versi IDE yang didukung ada di halaman produk HotPDF Component