HotPDF memverifikasi tanda tangan digital di dalam dokumen PDF yang dimuat melalui tiga metode THotPDF: GetLoadedSignatureInfo, VerifyLoadedSignature, dan VerifyLoadedSignatureEx, yang diperkenalkan pada v2.259.0. Component ini menghitung ulang hash segmen /ByteRange dari file aslinya, memeriksa atribut CMS messageDigest, dan menjalankan verifikasi RSA PKCS#1 v1.5 terhadap sertifikat penandatangan yang tertanam, lalu mengembalikan svValid ketika byte dokumen masih utuh
Skenarionya biasa saja, taruhannya tidak. Sebuah pihak lawan mengembalikan kontrak yang sudah ditandatangani, workflow Anda perlu mengarsipkannya, dan seseorang mengajukan satu-satunya pertanyaan yang penting: apakah ini dokumen yang kita kirim, byte demi byte, ditandatangani oleh sertifikat yang diklaimnya? Menjawab hal itu di dalam kode adalah sisi verifikasi dari cerita signature; sisi penandatanganannya, yakni membangun dan menanamkan signature PAdES sejak awal, dibahas di artikel pendamping tentang pembuatan tanda tangan digital PAdES dengan HotPDF. Artikel ini membahas arah sebaliknya: sebuah PDF tiba dalam keadaan sudah ditandatangani, dan Anda menginginkan putusan yang terprogram alih-alih tangkapan layar tanda centang hijau Acrobat
Bagaimana sebuah PDF yang ditandatangani membuktikan dirinya belum diutak-atik?
Sebuah signature PDF melindungi rentang byte tertentu di dalam file, bukan gagasan abstrak tentang "dokumen" itu. ISO 32000-1 §12.8 mendefinisikan mekanismenya: form field signature membawa sebuah dictionary yang entry /Contents-nya menyimpan container CMS SignedData (RFC 5652), dan array /ByteRange-nya menyebutkan region file persis yang dicakup signature tersebut, sesuai §12.8.1. Array itu adalah daftar pasangan offset dan panjang, dalam praktiknya dua segmen: semua yang berada sebelum string heksa /Contents, dan semua yang sesudahnya. Nilai signature tidak bisa mencakup dirinya sendiri, sehingga file di-hash mengelilingi lubang tersebut
Rancangan itu punya konsekuensi yang membentuk keseluruhan API-nya: verifikasi harus meng-hash byte terserialisasi yang asli, persis seperti byte itu tersimpan di disk. Model objek hasil parsing tidak berguna untuk keperluan ini, karena menserialisasi ulang dokumen yang bahkan tidak berubah sekalipun akan menghasilkan byte yang berbeda. Karena itu HotPDF melakukan verifikasi terhadap file sumber tempat dokumen dimuat, atau terhadap sebuah TStream berisi byte mentah yang Anda sediakan, tidak pernah terhadap representasi in-memory-nya
Membaca metadata signature sebelum memverifikasi apa pun
GetLoadedSignatureInfo mem-parsing dictionary signature beserta container CMS-nya tanpa menyentuh satu byte dokumen pun, sehingga inilah panggilan pertama yang tepat ketika Anda hanya perlu menampilkan siapa yang menandatangani dan kapan. Field signature diindeks mulai dari 0 dalam urutan form field, dan GetLoadedSignatureFieldCount memberi tahu berapa banyak yang ada. Record THPDFSignatureInfo yang dikembalikan membawa nama field, /SubFilter, common name pada sertifikat penandatangan, distinguished name untuk subject dan issuer, nomor seri, tanggal masa berlaku, waktu penandatanganan (dari signed attribute bila ada, jika tidak dari entry /M pada dictionary), nama algoritma digest, serta string /Reason, /Location, dan /ContactInfo. Anggota Status-nya tetap svNotVerified, sebuah label jujur untuk "sudah di-parsing, belum 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 kriptografisnya
VerifyLoadedSignatureEx melakukan verifikasi lengkap untuk dokumen yang dimuat dari file dan mengembalikan record info yang sudah terisi dalam satu panggilan: ia membuka ulang file sumber, meng-hash segmen /ByteRange dengan algoritma digest milik SignerInfo, membandingkan hasilnya dengan signed attribute messageDigest (RFC 5652 §5.4), lalu memverifikasi RSA atas signature terhadap hasil enkode ulang DER SET dari signed attributes tersebut. Ketika sebuah signature tidak membawa signed attributes, pemeriksaan RSA berjalan langsung atas hash dokumen. Signature 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 tool penandatanganan arus 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;
Ada dua detail implementasi yang layak diketahui karena keduanya menjelaskan kegagalan yang tampak misterius dari luar. Pertama, pemeriksaan signed attributes itu rewel soal enkode: di dalam file, atribut-atribut tersebut ditandai [0] IMPLICIT, namun signature-nya dihitung atas bentuk DER SET OF mereka, sehingga verifier menandai ulang sebelum meng-hash, persis seperti yang dituntut RFC 5652 §5.4. Sebuah verifier buatan sendiri yang meng-hash byte apa adanya sebagaimana muncul di file akan menolak setiap dokumen yang ditandatangani dengan benar. Kedua, /Contents secara konvensional diisi angka nol hingga memenuhi anggaran byte yang dicadangkan, sehingga verifier memangkas blob DER itu sampai panjang sebenarnya dari SEQUENCE terluarnya sebelum mem-parsing; deretan nol di ekor yang terlihat seperti sampah adalah hal normal, bukan korupsi. Keluarga bahaya parsing ASN.1 yang sama, pada sisi impor sertifikat, menjadi topik artikel tentang pengerasan keamanan PKCS#12 dan ASN.1 di HotPDF
Apa sebenarnya yang dijamin oleh signature yang valid?
svValid berarti persis ini: byte yang disebutkan oleh /ByteRange menghasilkan hash yang sama dengan nilai yang ditandatangani penandatangan, dan signature-nya terverifikasi di bawah public key dari sertifikat yang tertanam di dalam container CMS. Itu adalah integritas byte ditambah keterikatan key, tidak lebih. Validasi rantai sertifikat dan trust secara eksplisit berada di luar cakupan verifier HotPDF: ia tidak menelusuri rantai sampai ke root, tidak memeriksa revokasi, dan tidak berkonsultasi dengan trust store mana pun. Sertifikat self-signed dari penyerang yang menandatangani ulang dokumen yang telah diubah tetap akan terverifikasi sebagai svValid, karena matematikanya konsisten secara internal. Apakah penandatangan itu benar-benar seperti yang diakuinya, dan apakah ada yang pantas memercayainya, adalah keputusan kebijakan yang menjadi milik lapisan terpisah, entah itu whitelist sertifikat organisasi Anda, certificate store Windows, atau sebuah validation authority
Flag CoversWholeDocument menjaga celah yang lebih halus. Sebuah signature hanya pernah mencakup /ByteRange-nya, dan mekanisme incremental update pada PDF mengizinkan penambahan konten setelah sebuah signature tanpa membatalkannya, yang memang disengaja dan menjadi cara kerja workflow multi-signature. Flag itu dihitung saat verifikasi dan bernilai true hanya ketika kedua segmen ditambah celah /Contents merentang seluruh file. Ketika svValid datang dengan CoversWholeDocument bernilai false, revisi yang ditandatangani memang utuh namun file tersebut berisi tambahan yang datang belakangan, dan apa yang diubah oleh tambahan itu adalah sesuatu yang perlu diputuskan sendiri oleh workflow Anda: ditoleransi atau tidak
Dokumen yang dimuat dari stream dan dokumen terenkripsi butuh byte sumbernya sendiri
VerifyLoadedSignature dan VerifyLoadedSignatureEx versi tanpa parameter bergantung pada ingatan component tentang file asal dokumen tersebut. Muat dokumen dari sebuah stream, dan tidak ada nama file yang bisa dibuka ulang; hal yang sama berlaku setelah jalur reload berpassword yang dipakai untuk dokumen terenkripsi, workflow yang diuraikan di artikel tentang enkripsi PDF AES-256 dengan HotPDF. Pada kedua kasus itu, overload yang bersandar pada file mengembalikan svSourceUnavailable alih-alih menebak-nebak. Solusinya adalah overload TStream, yang membiarkan Anda menyerahkan byte mentah aslinya dari mana pun Anda menyimpannya, sebuah file yang masih Anda punya, buffer memori, atau blob di database
var
Src: TFileStream;
Status: THPDFSignatureVerifyStatus;
Info: THPDFSignatureInfo;
begin
// Dokumen yang dimuat dari stream: component tidak menyimpan nama
// file sumber, jadi sediakan sendiri byte aslinya.
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 bisa Anda verifikasi
Sebuah verifier yang hanya mengenal "valid" dan "tidak valid" akan salah melaporkan dokumen yang sekadar tidak dipahaminya, sehingga enumerasi status di sini memisahkan kasus-kasus yang seharusnya dibedakan oleh UI Anda. svDigestMismatch berarti byte dokumen berubah setelah penandatanganan, sinyal klasik adanya pengutak-atikan. svSignatureInvalid berarti hash byte-nya cocok namun pemeriksaan RSA gagal, yang menunjuk ke nilai signature yang rusak atau dipalsukan. svUnsupportedAlgorithm adalah jawaban jujur untuk key ECDSA dan digest yang tidak dikenali: signature-nya mungkin saja sangat baik, HotPDF sekadar tidak bisa memeriksanya, dan melaporkannya sebagai "tidak valid" berarti mencemarkan nama dokumen yang sehat. svMalformed menandai container CMS yang sama sekali tidak bisa di-parsing. Untuk pemeriksaan bergaya gerbang, VerifyAllLoadedSignatures mengembalikan true hanya ketika setidaknya ada satu field signature dan setiap satu di antaranya terverifikasi sebagai svValid, sebuah boolean tunggal yang praktis untuk pipeline penyerapan arsip yang menolak apa pun yang kurang dari itu
Verifikasi signature, penandatanganan PAdES, enkripsi AES-256, dan API penyuntingan dokumen yang dimuat semuanya hadir dalam satu library VCL native yang sama untuk Delphi dan C++Builder, tanpa ketergantungan DLL eksternal; daftar fitur lengkap dan versi IDE yang didukung ada di halaman produk HotPDF Delphi Component