💡 Contoh lengkap yang berfungsi tersedia di GitHub:
qr-sign-password-protected-pdf-python
Pendahuluan
Ada pola tiga langkah yang paling sering dipakai tim ketika dokumen yang harus ditandatangani ternyata terenkripsi: dekripsi, tanda tangani plaintext, lalu enkripsi kembali hasilnya. Pola ini memang berhasil. Namun artinya selama beberapa ratus milidetik salinan yang dapat dibaca dari dokumen yang sengaja dilindungi berada di direktori sementara, dan dalam pipeline yang diaudit jendela waktu itu menjadi temuan, bukan tanda tangan.
Menandatangani PDF yang dilindungi adalah kemampuan GroupDocs.Signature untuk Python via .NET yang melewati ketiga langkah tersebut sepenuhnya: kata sandi membuka sumber secara langsung, tanda tangan diterapkan, dan output ditulis kembali dalam keadaan terlindungi. Artikel ini membandingkan empat jalur kata sandi – dua yang berhasil dan dua yang sengaja gagal – serta membahas kontrak kegagalan yang khusus untuk binding ini.
Mengapa Ini Penting
Penanganan kata sandi adalah titik kebocoran pada pipeline dokumen. Bukan melalui pustaka penandatanganan, biasanya, melainkan melalui kerangka di sekitarnya: file sementara yang seharusnya dihapus, penangkap pengecualian yang menelan kesalahan kata sandi salah dan terus mencoba, serta salinan yang ditandatangani diserahkan dengan kata sandi yang tidak pernah diberitahukan kepada penerima.
Ketiganya memiliki akar penyebab yang sama, yaitu kata sandi diperlakukan sebagai sesuatu yang harus dihilangkan daripada menjadi bagian dari operasi. LoadOptions dan SaveOptions mengembalikannya ke dalam operasi.
Prasyarat
Python 3 dan groupdocs-signature-net==26.1, serta PDF dengan kata sandi pengguna. Tanpa lisensi pustaka berjalan dalam mode evaluasi, yang tetap menandatangani tetapi menambahkan teksnya sendiri ke halaman.
Instalasi
pip install groupdocs-signature-net==26.1
Metode 1 - Pertahankan kata sandi asli
Cara default, dan yang memerlukan kode paling sedikit. Kata sandi dimasukkan melalui LoadOptions, dan tidak ada SaveOptions yang diberikan sama sekali:
load_options = LoadOptions()
load_options.password = password
options = _build_qr_options(qr_text)
with signature.Signature(source_path, load_options) as sign:
result = sign.sign(output_path, options)
return len(result.succeeded)
Tidak adanya SaveOptions adalah yang melakukan pekerjaan sebenarnya di sini. use_original_password secara default bernilai True, sehingga GroupDocs menerapkan kembali kata sandi sumber ke output yang ditandatangani. Tidak ada momen di mana versi yang tidak terlindungi ada, baik di disk maupun di tempat lain, dan len(result.succeeded) melaporkan berapa banyak tanda tangan yang ditulis.
Metode 2 - Ganti kata sandi pada salinan yang ditandatangani
Ketika dokumen yang ditandatangani diberikan kepada pihak lain, langkah yang masuk akal adalah memberi salinan tersebut kredensialnya sendiri dan membiarkan sumber tetap tidak berubah:
save_options = SaveOptions()
save_options.password = new_password
save_options.use_original_password = False
with signature.Signature(source_path, load_options) as sign:
result = sign.sign(output_path, options, save_options)
return len(result.succeeded)
Kedua baris SaveOptions diperlukan, dan inilah detail yang perlu diingat: menetapkan password sambil membiarkan use_original_password pada nilai default tidak menghasilkan apa‑apa yang terlihat. Flag yang menang, output tetap memakai kata sandi lama, dan Anda akan menyadarinya ketika penerima melaporkan bahwa kata sandi yang Anda kirim tidak berfungsi.
Metode 3 dan 4 - Kedua kegagalan
Dokumen terenkripsi merespons secara berbeda terhadap kata sandi yang hilang dan kata sandi yang salah, dan perbedaan itu layak ditangani.
Tanpa LoadOptions sama sekali, pembukaan gagal dan tidak ada yang ditulis:
try:
with signature.Signature(source_path) as sign:
sign.sign(output_path, options)
return ""
except RuntimeError as error:
return proxy_error_name(error)
Itu mengembalikan PasswordRequiredException. Jika Anda memberikan kata sandi yang tidak tepat, kode yang sama mengembalikan IncorrectPasswordException. Yang satu berarti minta pengguna memasukkan kredensial; yang lain berarti kredensial yang Anda miliki sudah usang. Penangkap yang tidak dapat membedakan keduanya akan terus mencoba kata sandi yang tidak akan pernah berhasil.
Kontrak kegagalan, dan mengapa kode yang jelas menjadi rusak
Inilah bagian yang akan menghabiskan sore Anda jika tidak ada yang memperingatkan. Binding ini mengekspos PasswordRequiredException, IncorrectPasswordException, dan GroupDocsSignatureException sebagai nama mentah yang tidak mewarisi dari BaseException. Tulis penangkap yang intuitif:
except IncorrectPasswordException:
...
dan Python akan mengeluarkan TypeError: catching classes that do not inherit from BaseException is not allowed. Kesalahan asli hilang, digantikan oleh satu yang menunjuk ke baris except Anda alih‑alih ke kata sandi. Saya menulis penangkap persis seperti itu pada percobaan pertama, dan dua puluh menit yang saya habiskan membaca TypeError adalah alasan mengapa bagian ini ada.
Apa yang sebenarnya muncul adalah RuntimeError dengan pesan yang dimulai Proxy error(<Name>): . Mengurai awalan itu mengembalikan penyebabnya:
message = str(error)
marker = "Proxy error("
if not message.startswith(marker):
return ""
start = len(marker)
end = message.find(")", start)
if end < 0:
return ""
return message[start:end]
Cabang berdasarkan nama yang dikembalikan, bukan pada teks pesan, yang berisi jalur file dan dapat berubah‑ubah antar‑run.
Memeriksa sebelum menandatangani
Ada jalur kelima yang patut diketahui, dan jalur ini tidak menulis apa‑apa sama sekali. Membuka dokumen dengan LoadOptions dan memanggil get_document_info mengembalikan format, jumlah halaman, dan ukuran sementara file tetap terenkripsi di disk:
with signature.Signature(source_path, load_options) as sign:
info = sign.get_document_info()
return info.file_type.file_format, info.page_count, info.size
Dua kegunaan untuk ini. Ketika kata sandi berasal dari formulir pengguna, ini memvalidasi kredensial dengan panggilan yang murah daripada menunggu setengah ratus dokumen selesai diproses. Dan ketika sebuah pipeline tidak diizinkan menyimpan plaintext sama sekali, ia tetap memungkinkan pipeline tersebut melaporkan apa yang sedang dipegangnya – jumlah halaman untuk log audit, ukuran untuk kuota – tanpa mendekripsi apa pun.
Membandingkan Metode: Kapan Menggunakan Masing‑Masing
| Metode | Terbaik Untuk | Keunggulan Utama | Keterbatasan |
|---|---|---|---|
| Keep original password | pipeline yang menandatangani di tempat | tidak ada SaveOptions, tidak ada yang ditulis dalam bentuk jelas |
penerima membutuhkan kata sandi sumber |
| Re-key on save | penyerahan ke pihak lain | sumber tetap memakai kredensialnya, salinan mendapat yang baru | dua baris SaveOptions, mudah salah mengatur hanya satu |
| No password (fails) | membuktikan kontrak dalam pengujian | gagal saat membuka, tidak menulis apa‑apa | bukan jalur penandatanganan |
| Wrong password (fails) | membedakan kredensial yang usang | nama pengecualian yang berbeda | bukan jalur penandatanganan |
Apakah membaca kembali layak panggilan tambahan?
Ya, untuk dua alasan. Membuka kembali file yang ditandatangani dengan QrCodeVerifyOptions membuktikan tanda tangan bertahan setelah disimpan, dan karena pembukaan kembali harus menyertakan kata sandi, itu juga membuktikan output memang masih terenkripsi. Hitungan nol hampir selalu menandakan masalah lisensi, bukan kegagalan penandatanganan – panggilan sign akan mengeluarkan pengecualian ketika benar‑benar gagal, sehingga tidak ada output sekaligus hitungan nol mengindikasikan build yang tidak berlisensi.
Apa Biayanya untuk Beralih
Tidak ada perubahan struktural. Jika kode Anda sudah mendekripsi ke file sementara, perubahan yang diperlukan hanyalah menghapus langkah itu, memindahkan kata sandi ke dalam LoadOptions, dan menghapus panggilan re‑enkripsi di akhir – biasanya menghasilkan pengurangan baris kode. Panggilan penandatanganan itu sendiri tidak berubah bentuk, dan outputnya byte‑per‑byte adalah PDF yang ditandatangani dengan perlindungan yang sama seperti saat masuk.
Satu hal yang perlu diperhatikan dengan saksama adalah kode pembersihan. Pipeline yang dibangun di sekitar decrypt‑sign‑reencrypt biasanya memiliki blok finally yang menghapus file sementara, dan begitu file sementara itu hilang, blok tersebut akan mencoba menghapus jalur yang tidak lagi ada.
Praktik Terbaik
- Biarkan
use_original_passwordapa adanya kecuali Anda memang ingin merotasi; nilai default adalah yang paling aman. - Parse nama proxy sekali, dalam sebuah helper, dan cabang berdasarkan nama itu di seluruh tempat lain.
- Validasi kata sandi yang diberikan pengguna dengan
get_document_infosebelum memulai batch, sehingga kredensial yang buruk hanya menelan satu panggilan murah, bukan setengah proses yang gagal. - Jangan pernah menulis output yang ditandatangani ke jalur sumber, sehingga kesalahan tidak menimpa file asli yang masih dapat dipulihkan.
Kesimpulan
Kata sandi bukanlah hambatan yang harus diatasi sebelum menandatangani – ia adalah argumen bagi operasi. Buka dengan LoadOptions, tentukan perlindungan output dengan SaveOptions, parse nama proxy ketika sesuatu gagal, dan verifikasi melalui kata sandi setelahnya. Contoh ini menjalankan keempat jalur dalam satu kali eksekusi, sehingga perbedaan di antara mereka dapat dilihat dengan satu perintah, bukan harus membaca paragraf panjang.