💡 Contoh kerja penuh tersedia di GitHub:
digital-signing-certificate-validity-dotnet

Masalah Kepatuhan yang Tidak Terlihat Sampai Auditor Menemukannya

Layanan penandatanganan berjalan selama tiga tahun tanpa kesalahan. Dokumen dikirim, penerima menerimanya, tidak ada yang mencurigakan di log. Kemudian validator pihak lawan menandai satu batch sebagai tidak valid, dan penyelidikan menemukan dua penyebab: tanda tangan dibuat dengan SHA‑1, dan selama empat bulan terakhir sertifikat sudah kedaluwarsa.

Kedua kegagalan tersebut tidak menimbulkan peringatan pada saat penandatanganan. Inilah yang diubah oleh GroupDocs.Signature 26.9.

Penegakan keabsahan sertifikat menjadi perilaku default baru untuk penandatanganan digital .NET: sertifikat di luar jendela keabsahannya ditolak alih‑alih digunakan. Fitur ini hadir bersama dua pendamping — SHA‑256 sebagai digest PDF default, dan LogLevel yang akhirnya menyaring — dan bersama‑sama mereka memindahkan tiga kelas kegagalan dari penerima kembali ke pengirim, di mana masih dapat diperbaiki.

Mengapa Keberhasilan Tanpa Peringatan Menjadi Hasil yang Mahal

Penandatanganan tidak biasa karena pihak yang membuat kesalahan bukanlah pihak yang menemukannya. Faktur yang rusak gagal di sistem Anda; tanda tangan yang tidak valid gagal di sistem orang lain, beberapa minggu kemudian, tanpa diagnostik yang dapat Anda baca.

Asimetri inilah yang membuat “API mengembalikan sukses” tidak menjadi jaminan yang berguna di sini. Default lama dioptimalkan agar tidak mengganggu pemanggil, dan biaya jatuh pada penerima serta, pada akhirnya, pada siapa pun yang harus menandatangani ulang dan mengirim kembali ratusan dokumen.

Perubahan 1: Sertifikat Kedaluwarsa Ditolak

Perubahan utama. Sign kini melempar GroupDocsSignatureException ketika masa berlaku sertifikat telah berakhir atau belum dimulai, dan tidak ada apa‑apa yang ditulis ke disk.

try
{
    signature.Sign(outputPath, options);
    return true;
}
catch (GroupDocsSignatureException ex)
{
    Console.WriteLine($"   Rejected: {ex.Message}");
    return false;
}

Pesan tersebut menyebutkan sertifikat dan properti yang akan mengizinkannya, sehingga operator yang membaca baris log dapat menindaklanjutinya tanpa membuka dokumentasi. Untuk pipeline yang diupgrade ke 26.9 dan mulai gagal, ini hampir selalu penyebabnya — dan respons yang tepat adalah memperbarui, bukan menekan.

Jika Anda memang memerlukan perilaku lama, cukup tambahkan satu properti:

var options = new DigitalSignOptions(certificate)
{
    Password = certificatePassword,
    AllowExpired = true
};

Dokumen tetap ditandatangani dan peringatan dikirim ke logger. Validator tetap menolak hasilnya, karena AllowExpired mengatur apa yang diizinkan perpustakaan, bukan nilai sertifikat itu sendiri. Flag pendamping AllowNotYetValid menutupi ujung lain dari jendela dan sengaja independen: mengizinkan sertifikat kedaluwarsa tidak secara otomatis mengizinkan sertifikat yang masih akan berlaku.

Perubahan 2: SHA‑256 Secara Default

Tanda tangan digital PDF kini ditulis dengan SHA‑256 dalam format adbe.pkcs7.detached yang diharapkan validator saat ini. Versi sebelumnya menulis dengan SHA‑1.

var options = new DigitalSignOptions(certificate)
{
    Password = certificatePassword,
    HashAlgorithm = HashAlgorithm.Sha256,
    Reason = "Approved",
    Location = "Head office"
};

Menetapkan properti secara eksplisit hanya diperlukan bila ingin melangkah lebih jauh — Sha384 atau Sha512 ketika kebijakan memerlukannya — atau tetap pada Sha1 untuk validator yang tidak dapat menangani yang lain. Stempel waktu yang ditambahkan ke tanda tangan menggunakan digest yang sama.

Verifikasi juga berubah dalam rilis yang sama dan ke arah yang sama: DigitalVerifyOptions tanpa kriteria sebelumnya hampir tidak melakukan apa‑apa, kini melakukan pemeriksaan kriptografis penuh, sehingga dokumen yang diubah setelah penandatanganan dilaporkan tidak valid.

Perubahan 3: LogLevel Benar‑benar Menyaring

SignatureSettings telah menerima logger sejak lama. Sebelum 26.9 levelnya diabaikan, sehingga setiap pesan muncul terlepas dari level, dan kebanyakan layanan mematikan logging daripada tenggelam dalam jejak.

Contoh ini membuat perbedaan dapat diukur dengan menandatangani dokumen yang sama tiga kali menggunakan logger yang menghitung:

var levels = new Dictionary<string, LogLevel>
{
    ["None"] = LogLevel.None,
    ["Warning | Error"] = LogLevel.Warning | LogLevel.Error,
    ["All"] = LogLevel.All
};

None menghasilkan nol pesan, Warning | Error hanya menampilkan peringatan tunggal yang muncul karena sertifikat kedaluwarsa yang diizinkan, dan All menambahkan jejak pada setiap langkah. Logger yang menghitung inilah titik integrasi untuk stack Anda sendiri:

public void Warning(string message)
{
    Warnings++;
    WarningMessages.Add(message);
}

Implementasikan ketiga metode tersebut dengan Serilog, NLog, atau Application Insights dan diagnostik perpustakaan akan muncul di mana pun log layanan Anda berada.

Apakah level log mengubah pengecualian yang saya dapatkan?

Tidak, dan penting untuk dijelaskan karena keduanya tampak terkait. LogLevel menyaring apa yang sampai ke ILogger. Pengecualian tetap dilempar ke kode Anda: sertifikat kedaluwarsa tanpa AllowExpired tetap melempar meskipun LogLevel.None, dan blok catch Anda berperilaku sama. Diagnostik dan alur kontrol adalah saluran terpisah, itulah yang membuatnya aman menjalankan produksi dengan Warning | Error.

Penolakan Lebih Murah Daripada yang Terlihat

Keberatan terhadap penghentian keras bersifat operasional: batch malam yang dulu selesai kini gagal pada pukul 02:00 dan seseorang dipanggil. Itu memang biaya nyata, dan masih lebih kecil. Batch yang ditolak menghasilkan satu peringatan, satu perpanjangan, dan satu kali jalankan ulang, semuanya di dalam sistem Anda. Batch yang ditandatangani dengan sertifikat kedaluwarsa ditemukan oleh penerima, yang berarti ada thread dukungan, penerbitan ulang setiap dokumen yang terdampak, dan percakapan canggung tentang berapa lama hal itu sudah terjadi.

Contoh ini membuat kegagalan menjadi konkret, bukan teoretis: ia menandatangani dengan sengaja menggunakan sertifikat kedaluwarsa, menangkap pengecualian, dan mencetak pesannya, sehingga Anda dapat melihat persis apa yang akan muncul di log sebelum peningkatan mencapai produksi. Saya sarankan menjalankan metode itu dengan penyimpanan sertifikat Anda sendiri sebelum menjadwalkan peningkatan versi.

Apa yang Harus Dilakukan Sebelum Memperbarui

Tiga pemeriksaan, urut berdasarkan seberapa besar kemungkinan mengganggu.

  1. Periksa masa kedaluwarsa sertifikat di setiap jalur penandatanganan, termasuk yang dijalankan bulanan atau kuartalan — di situlah sertifikat kedaluwarsa biasanya bersembunyi paling lama.
  2. Cari HashAlgorithm: jika tidak ada yang menyetelnya, digest Anda akan berubah dari SHA‑1 ke SHA‑256 pada upgrade, yang merupakan perbaikan yang tetap perlu dicatat dalam catatan rilis.
  3. Tentukan level log secara sengaja. Default jujur untuk layanan adalah Warning | Error; All dipakai untuk mereproduksi masalah tertentu, dan None berarti mengorbankan sinyal satu‑satunya yang memberi tahu Anda bahwa tanda tangan dibuat dengan pengecualian.

Verifikasi Berubah ke Arah yang Sama

Mudah terlewat, karena tidak ada kode pemanggil yang harus diubah. DigitalVerifyOptions tanpa kriteria sebelumnya hampir tidak melakukan apa‑apa: ia membandingkan kriteria yang diberikan, dan bila tidak ada, ia tidak banyak berkata. Mulai versi 26.9, panggilan yang sama melakukan pemeriksaan kriptografis penuh pada setiap tanda tangan digital PDF.

Bagi layanan yang memverifikasi dokumen masuk, ini merupakan peningkatan diam dari “ada tanda tangan di sini” menjadi “tanda tangan ini cocok dengan konten ini”. Penting diketahui sebelum Anda melihat dokumen yang dulu lolos verifikasi kini gagal: dokumen tersebut kemungkinan telah diubah, dan pemeriksaan lama memang tidak memeriksanya.

Sertifikat dalam Contoh

Satu detail yang patut disalin daripada kode: contoh tidak menyertakan kunci pribadi. TestCertificates.cs membuat tiga PFX self‑signed di memori saat runtime — valid, kedaluwarsa tahun lalu, dan berlaku mulai tahun depan — sehingga demonstrasi berfungsi apa pun tanggal hari ini dan tidak ada data sensitif dalam repositori.

Pola ini layak diadopsi dalam suite pengujian Anda. Sertifikat uji yang dikomitmen akan kedaluwarsa pada akhirnya, dan ketika itu terjadi kegagalan akan tampak persis seperti bug yang ingin diungkapkan rilis ini.

Kesimpulan

Tiga perubahan, satu arah: kegagalan yang dulu muncul di penerima kini muncul di pengirim. Perpanjang sertifikat alih‑alih menggunakan AllowExpired, jadikan SHA‑256 sebagai default, verifikasi dokumen masuk secara kriptografis, dan pilih level log sebelum Anda membutuhkannya. Contoh menjalankan keenam perilaku dalam satu kali eksekusi, termasuk penolakan, sehingga upgrade dapat dipraktekkan dalam beberapa menit.

Sumber Daya Tambahan