💡 Contoh kerja penuh tersedia di GitHub:
pdf-signing-certificate-checks-python

Pendahuluan

Sebuah layanan menandatangani PDF yang diunggah setiap malam. Suatu pagi sertifikat yang digunakannya melewati tanggal kedaluwarsa, dan tidak ada yang tampak berubah: pekerjaan berjalan, file ditulis, log terlihat normal. Beberapa minggu kemudian seseorang membuka salah satu dokumen itu di Acrobat dan melihat banner peringatan, karena tanda tangan yang dibuat dengan sertifikat kedaluwarsa bukanlah tanda tangan yang lebih lemah — itu adalah tanda tangan yang dilaporkan validator sebagai tidak sah. Dokumen yang tampak disetujui menjadi kurang berharga daripada yang tidak ditandatangani, karena orang mempercayainya.

Penolakan itu memiliki nama. Pemeriksaan keabsahan sertifikat adalah perilaku GroupDocs.Signature untuk Python yang menolak menandatangani begitu periode keabsahan sertifikat telah berakhir, atau sebelum dimulai. Fitur ini muncul pada versi 26.9 bersamaan dengan dua perubahan lain dengan bentuk yang sama: SHA‑256 menjadi digest default untuk tanda tangan PDF, dan SignatureSettings.log_level mulai menyaring alih‑alih diam‑diam diabaikan. Masing‑masing mengubah hasil yang sebelumnya terjadi secara diam menjadi sesuatu yang ditampilkan di depan Anda.

Artikel ini membandingkan ketiga kontrol tersebut sebagaimana berperilaku dari Python melalui .NET — apa yang diubah masing‑masing dalam output, kapan harus menggunakannya, dan dua detail binding yang membuat orang menghabiskan sore. Semua hasil yang dikutip berasal dari menjalankan contoh pada PDF satu halaman.

Mengapa Ini Lebih Penting Daripada Catatan Versi

Ketiga perubahan tersebut memiliki sifat yang layak disebutkan: semuanya mengubah kegagalan yang biasanya Anda temukan kemudian menjadi kegagalan yang Anda temukan sekarang.

  • Sertifikat kedaluwarsa: panggilan penandatanganan gagal sehingga seseorang dapat memperbarui sertifikat, alih‑alih menghasilkan dokumen yang gagal validasi setelah didistribusikan
  • Digest default: tanda tangan baru menggunakan SHA‑256 tanpa ada yang mengingat untuk memintanya, sehingga opsi lemah memerlukan keputusan bukan kelalaian
  • Level log: layanan yang mengonfigurasi hanya peringatan kini menerima hanya peringatan, yang membuat peringatan dapat dibaca, yang berarti mereka dibaca

Poin terakhir bukan sekadar kosmetik. Nilai penuh dari peringatan sertifikat kedaluwarsa adalah seseorang melihatnya, dan peringatan yang terkubur di antara sepuluh pesan jejak per proses penandatanganan adalah peringatan yang tidak pernah dilihat.

Prasyarat

Sebelum memulai, pastikan Anda memiliki:

  • Python 3.9 atau lebih baru pada interpreter 64‑bit — paket ini menyertakan runtime .NET yang dibundel dan tidak memiliki wheel 32‑bit
  • GroupDocs.Signature untuk Python via .NET 26.10.0, dengan lisensi sementara gratis jika Anda ingin menghapus batas evaluasi
  • Sebuah PDF untuk ditandatangani, dan paket cryptography jika Anda ingin membuat sertifikat uji pakai sekali seperti contoh

Instalasi

pip install groupdocs-signature-net cryptography

Kontrol 1 - Digest yang ditulis ke dalam tanda tangan

hash_algorithm pada DigitalSignOptions memilih digest. Default sejak 26.9 adalah SHA‑256, dalam format adbe.pkcs7.detached yang saat ini diharapkan validator; sebelum itu, tanda tangan baru menggunakan SHA‑1.

with signature.Signature(source_path) as sign:
    options = DigitalSignOptions()
    options.certificate_stream = io.BytesIO(pfx)
    options.password = PASSWORD
    options.hash_algorithm = HashAlgorithm.SHA512
    options.reason = "Approved"

    result = sign.sign(output_path, options)
    return len(result.succeeded)

Dua detail penting untuk dicatat. Sertifikat masuk melalui certificate_stream sebagai io.BytesIO alih‑alih jalur file, yang merupakan cara PKCS#12 yang dibangun di memori mencapai pustaka tanpa pernah ditulis ke disk — contoh mengandalkan hal ini sehingga tidak mengirimkan kunci pribadi sama sekali. Dan HashAlgorithm menawarkan AUTO, SHA1, SHA256, SHA384, dan SHA512, di mana cap waktu, bila Anda menambahkannya, menggunakan digest apa pun yang dipakai tanda tangan.

Dalam praktik, ini adalah kontrol yang paling jarang Anda sentuh. Defaultnya sudah merupakan jawaban yang tepat, SHA384 dan SHA512 ada untuk kebijakan penandatanganan yang menyebutkannya, dan SHA1 adalah pengaturan kompatibilitas untuk validator yang tidak dapat Anda ubah.

Kontrol 2 - Apakah sertifikat kedaluwarsa menghentikan Anda

Tanpa override, menandatangani dengan sertifikat yang periode keabsahannya telah berakhir — atau belum dimulai — menimbulkan GroupDocsSignatureException dan tidak menulis apa‑apa.

try:
    sign.sign(output_path, options)
    return True
except signature.GroupDocsSignatureException as error:
    print(f"Rejected: {str(error).splitlines()[0]}")
    return False

Pesan tersebut menyebutkan nama sertifikat, tanggal kedaluwarsa, sidik jari, dan properti yang akan memperbolehkannya, yang cukup bagi aplikasi untuk memberi tahu operator apa yang harus diperbarui. Mengambil hanya baris pertama penting khususnya di Python: teks pengecualian berlanjut dengan jejak tumpukan .NET di belakang binding, dan itu bukan sesuatu yang ingin Anda tunjukkan kepada pengguna.

Ketika Anda memang harus menandatangani — misalnya uji dengan sertifikat arsip, atau batch yang harus dijalankan malam ini sementara perpanjangan sedang diproses — override dilakukan per panggilan:

settings = signature.SignatureSettings(ConsoleLogger())
settings.log_level = LogLevel.WARNING | LogLevel.ERROR

with signature.Signature(source_path, settings=settings) as sign:
    options.allow_expired = True
    result = sign.sign(output_path, options)

allow_not_yet_valid memiliki bentuk yang sama untuk sertifikat yang diterbitkan untuk tanggal mendatang, dan dua flag tersebut independen: memperbolehkan sertifikat kedaluwarsa tidak memperbolehkan sertifikat yang masih terlalu awal. Sertifikat yang terlalu awal biasanya berarti jam mesin salah, bukan sertifikat yang tidak biasa, dan jam yang salah membuat setiap tanda tangan yang dihasilkan mesin tersebut dipertanyakan, jadi periksa hal itu sebelum menimpa apa pun.

Kedua override menghasilkan peringatan alih‑alih lewat diam‑diam, yang merupakan bagian yang terhubung ke kontrol ketiga.

Kontrol 3 - Apakah ada yang menemukan

SignatureSettings.log_level adalah nilai flag. Contoh menandatangani dokumen yang sama tiga kali, dengan LogLevel.NONE, LogLevel.WARNING | LogLevel.ERROR, dan LogLevel.ALL, menghitung apa yang muncul:

logger = CollectingLogger()
settings = signature.SignatureSettings(logger)
settings.log_level = level

with signature.Signature(source_path, settings=settings) as sign:
    options.allow_expired = True
    sign.sign(output_path, options)

Hasil hitungannya menjadi tidak ada apa‑apa, kemudian satu peringatan, kemudian peringatan itu ditambah sepuluh jejak. Sebelum 26.9 ketiga baris itu akan identik, karena level diterima dan diabaikan — yang penting diketahui jika Anda pernah mengaturnya, tidak melihat perubahan, dan menyimpulkan bahwa Anda salah membaca kode sendiri.

Dua detail binding membuat saya menghabiskan sore, jadi sebaiknya dijelaskan secara jelas. SignatureSettings.logger bersifat read‑only, sehingga logger harus diberikan sebagai argumen konstruktor dan penetapan ulang akan menimbulkan AttributeError; log_level diatur secara normal setelahnya. Dan logger kustom tidak boleh mewarisi groupdocs.signature.logging.ILogger — kelas dasar itu membungkus objek native yang konstruktor memerlukan handle yang dimiliki pustaka, sehingga pewarisan menimbulkan TypeError. Binding menerima objek apa pun yang menyediakan tiga metode berikut:

class StdlibLogger:
    def error(self, message, exception=None):
        logging.getLogger("groupdocs").error(message, exc_info=exception)

    def warning(self, message, exception=None):
        logging.getLogger("groupdocs").warning(message)

    def trace(self, message):
        logging.getLogger("groupdocs").debug(message)

Berikan error dan warning parameter opsional exception. Pustaka tidak selalu mengirimkan satu, dan logger yang memerlukannya akan gagal pada pesan yang tidak menyertakannya.

Membandingkan Ketiganya: Kapan Menggunakan Masing‑Masing

Kontrol Terbaik untuk Keunggulan utama Batasan
hash_algorithm memenuhi kebijakan yang menyebutkan digest satu penetapan; ukuran output sama tidak berguna bila sertifikatnya sendiri tidak dipercaya
validitas & override segala penandatanganan untuk orang lain kegagalan muncul di tempat yang dapat diperbaiki override menghasilkan file, bukan file yang dapat dipercaya
log_level layanan yang lognya sudah sibuk sebelas pesan menjadi satu hanya menyaring log, tidak pernah menyaring pengecualian

Mereka bukan alternatif — satu panggilan penandatanganan menggunakan ketiganya. Urutan pemikiran yang tepat adalah urutan konsekuensi: pemeriksaan validitas menentukan apakah file ada, digest menentukan apa isi di dalamnya, dan level log menentukan siapa yang tahu.

Apakah level log mengubah pengecualian yang saya dapatkan?

Tidak. Level log menentukan pesan mana yang sampai ke logger Anda dan tidak lebih. Sertifikat kedaluwarsa tetap menimbulkan GroupDocsSignatureException di bawah LogLevel.NONE, dan allow_expired tetap menandatangani di bawah LogLevel.ALL; nilai kembali dan pengecualian identik di semua level. Yang berubah hanyalah apakah peringatan yang menjelaskan tanda tangan yang dipertanyakan pernah dibaca oleh seseorang.

Verifikasi Dipindahkan ke Arah yang Sama

Patut disebut karena ini adalah setengah lain dari rilis yang sama. verify dengan DigitalVerifyOptions kosong kini memeriksa setiap tanda tangan digital PDF secara kriptografis, sehingga dokumen yang diubah setelah penandatanganan menjadi tidak sah alih‑alih hanya tidak dijelaskan:

with signature.Signature(signed_path) as sign:
    result = sign.verify(DigitalVerifyOptions())
    return result.is_valid

Dua baris, dan layak ditambahkan ke setiap pipeline yang menandatangani lalu menyimpan. Perhatikan apa yang tidak dijanjikan oleh True: ia menyatakan tanda tangan cocok dengan dokumen, bukan bahwa penerbitnya dipercaya. Sertifikat self‑signed pada contoh diverifikasi di sini dan masih ditolak oleh pembaca PDF, yang menjawab pertanyaan kepercayaan secara terpisah.

Praktik Terbaik dan Tips

  • Jadikan penolakan sebagai default dalam segala hal yang menandatangani atas nama pengguna, dan lakukan override per panggilan, bukan secara global. Pengecualian murah; batch tanda tangan tidak sah tidak.
  • Log teks peringatan, bukan hanya hitungan. Ia menyebutkan sertifikat dan tanggalnya, satu‑satunya bagian yang dapat ditindaklanjuti operator.
  • Periksa jam sebelum memperbolehkan sertifikat yang belum valid. Sertifikat biasanya benar dan mesin biasanya salah, dan hal itu memengaruhi lebih dari satu panggilan penandatanganan.
  • Jangan biarkan jejak masuk ke produksi. Sekitar sepuluh jejak per proses menandatangani cepat menumpuk; aktifkan saat diagnosis dan matikan setelahnya.
  • Verifikasi setelah menandatangani dalam setiap pipeline, kini pemeriksaannya kriptografis, sehingga output yang rusak tertangkap sebelum penerima menemukannya.

Kesimpulan

Tiga kontrol, satu panggilan penandatanganan, dan gagasan desain yang sama di balik semuanya: hasil berisiko kini memerlukan keputusan, dan hasil aman tidak memerlukan apa‑apa. Pertahankan pemeriksaan validitas, perlakukan allow_expired sebagai pengecualian per‑panggilan yang Anda log, biarkan digest apa adanya kecuali kebijakan menyatakan lain, dan atur level log sehingga peringatan dapat dibaca.

Menjalankan contoh pada salah satu PDF Anda sendiri memakan waktu sekitar satu menit dan mencetak tepat apa yang diubah tiap kontrol — enam file tertanda, satu penolakan sengaja, dan tiga baris hitungan pesan yang tidak lagi sama.

Sumber Daya Tambahan