Artikel Teknis

PDFium VCL Remote PAdES Signing: HSM dan Cloud Key

PDFiumPas memecah proses signing PAdES menjadi dua pemanggilan sehingga private key tidak pernah harus berada di dalam process Anda. PreparePadesRemoteSignature menulis sebuah incremental update dengan placeholder /Contents berlebar tetap yang kosong dan mengembalikan sebuah request record yang membawa document digest SHA-256, ByteRange yang persis, dan fingerprint dari file yang sudah dipersiapkan. CompletePadesRemoteSignature mengambil CMS detached yang dikembalikan oleh signing service Anda dan menaruhnya ke dalam slot yang dicadangkan itu

Di antara kedua pemanggilan itu, bisa berlalu beberapa menit atau jam, process bisa restart, dan pekerjaannya bisa berpindah ke mesin lain. Celah itulah keseluruhan alasan mengapa API-nya dirancang seperti ini

Mengapa key remote tidak bisa memakai pemanggilan signing biasa?

Karena SignPadesBytes mengasumsikan operasi signing terjadi di dalam pemanggilan itu sendiri. Ia membangun incremental update, menghitung digest atas ByteRange, menandatanganinya, dan menulis hasilnya, semuanya sebelum kembali. Itu persis benar saat key-nya berada di Windows certificate store atau di dalam file PKCS#12 yang Anda muat

Itu menjadi mustahil saat key-nya berada di network HSM, sebuah qualified signature-creation device yang dioperasikan oleh trust service provider, atau sebuah cloud signing API yang mengharuskan pengguna melakukan konfirmasi lewat telepon. Dalam kasus-kasus itu urutannya bukan sebuah pemanggilan fungsi, melainkan sebuah percakapan: Anda mengirim sebuah digest, sesuatu yang lain mengautentikasi seorang manusia, dan sebuah CMS kembali belakangan. API sinkron tidak bisa mengekspresikan "belakangan" tanpa memblokir sebuah thread pada operasi yang mungkin membutuhkan faktor kedua

Protokol dua fase

Fase pertama mempersiapkan dokumen. PDFiumPas menambahkan signature field dan value dictionary, mencadangkan ContentsSize byte ruang hex-encoded di dalam /Contents, menghitung ByteRange di sekitar cadangan itu, dan menghasilkan sebuah TPadesRemoteSigningRequest yang berisi FormatVersion, PreparedFingerprint, DocumentDigest, ByteRange empat elemen, ContentsHexOffset, dan ContentsSize

Satu-satunya nilai yang dibutuhkan signing service Anda adalah DocumentDigest: SHA-256 yang harus dibawa oleh CAdES SignedData yang dikembalikan sebagai message digest-nya. Semua yang lain di dalam record itu ada agar fase dua bisa membuktikan bahwa file yang sedang diselesaikannya adalah file yang darinya digest itu dihitung

uses
  FPdfPades;

var
  Options: TPadesRemoteSignOptions;
  Request: TPadesRemoteSigningRequest;
  Source, Prepared, Session: TFileStream;
begin
  Options := TPadesRemoteSignOptions.Default;
  Options.Reason := 'Approved by finance';
  Options.Location := 'Lisbon';
  Options.Name := 'A. Moreira';
  Options.SigningTimeUtc := NowUtc;
  Options.ContentsSize := 16384;   // byte hex yang dicadangkan untuk CMS

  Source := TFileStream.Create('contract.pdf', fmOpenRead or fmShareDenyWrite);
  Prepared := TFileStream.Create('contract.prepared.pdf', fmCreate);
  try
    PreparePadesRemoteSignature(Source, Prepared, Options, Request);
  finally
    Prepared.Free;
    Source.Free;
  end;

  // Simpan session-nya agar run yang belakangan - atau mesin lain - bisa menyelesaikannya
  Session := TFileStream.Create('contract.signreq', fmCreate);
  try
    SavePadesRemoteSigningRequest(Session, Request);
  finally
    Session.Free;
  end;

  SendDigestToSigningService(Request.DocumentDigest);
end;

Apa yang ditolak Complete, dan mengapa setiap pemeriksaan itu ada?

Tahap completion adalah tempat desain remote signing biasanya bermasalah, sehingga validasinya sengaja dibuat tidak memberi ampun. CompletePadesRemoteSignature menolak PDF yang sudah dipersiapkan namun fingerprint-nya tidak lagi cocok dengan request, ByteRange yang tidak cocok dengan koordinat placeholder yang tercatat, delimiter /Contents yang dimodifikasi, placeholder yang sudah tidak kosong lagi, CMS yang lebih besar dari cadangannya, CMS yang bukan tepat satu nilai DER, bentuk SignedData yang tidak didukung, atribut signing-certificate-v2 yang hilang, dan CMS yang message digest-nya tidak sama dengan document digest yang dipersiapkan

Setiap poin itu berkorespondensi dengan sebuah kegagalan nyata. Pemeriksaan fingerprint dan ByteRange menangkap kasus di mana seseorang meregenerasi file yang dipersiapkan itu di antara kedua fase, yang akan menghasilkan sebuah signature yang tervalidasi terhadap byte yang tidak dimiliki siapa pun. Pemeriksaan placeholder-kosong menangkap completion ganda, di mana CMS kedua ditulis di atas signature yang sudah ada. Pemeriksaan message-digest menangkap kasus paling berbahaya dari semuanya: sebuah CMS yang terbentuk dengan benar tapi ditandatangani atas dokumen yang berbeda, yaitu yang Anda dapatkan saat sebuah queue mencampuradukkan dua signing session yang berjalan bersamaan. Tanpa itu Anda akan menghasilkan file yang tampak sudah ditandatangani dan gagal validasi di mana-mana, atau lebih buruk lagi, yang membawa persetujuan milik orang lain

Persyaratan signing-certificate-v2 adalah persoalan conformance PAdES, bukan persoalan integritas. ETSI EN 319 142 mensyaratkan sertifikat penandatangan diikat ke dalam signed attribute, dan CMS yang tidak memiliki atribut itu bukanlah signature PAdES sekalipun ia terverifikasi secara kriptografis. Menolaknya pada tahap completion berarti Anda mengetahuinya di sini, bukan dari laporan validator seorang pelanggan, topik yang dibahas lebih lanjut di mengapa validator menolak signature PAdES

var
  Request: TPadesRemoteSigningRequest;
  Session, Prepared, Dest: TFileStream;
  CmsDer: TBytes;
begin
  Session := TFileStream.Create('contract.signreq', fmOpenRead);
  try
    Request := LoadPadesRemoteSigningRequest(Session);
  finally
    Session.Free;
  end;

  CmsDer := FetchDetachedCmsFromService;   // dikembalikan oleh HSM atau TSP

  Prepared := TFileStream.Create('contract.prepared.pdf', fmOpenRead);
  Dest := TFileStream.Create('contract.signed.pdf', fmCreate);
  try
    try
      CompletePadesRemoteSignature(Prepared, Dest, Request, CmsDer);
    except
      on E: EPadesCrypto do
        // Setiap penolakan membawa alasan spesifik; catat apa adanya
        FailSession(E.Message);
    end;
  finally
    Dest.Free;
    Prepared.Free;
  end;
end;

Melintasi batas process dan mesin

SavePadesRemoteSigningRequest dan LoadPadesRemoteSigningRequest menyerialisasi session melalui format binary bervensi yang stabil, dan itulah yang membuat desainnya praktis, bukan sekadar benar. Sebuah aplikasi web bisa mempersiapkan sebuah dokumen dalam satu request, menyimpan PDF yang sudah dipersiapkan dan blob session-nya, mengembalikan sebuah digest ke browser untuk signature smart-card, dan menyelesaikan file itu di request handler yang sama sekali berbeda

Field FormatVersion adalah yang menjaga hal itu tetap aman di seluruh upgrade. Session yang ditulis oleh build lama dan dimuat oleh build baru akan dikenali atau ditolak secara eksplisit, alih-alih dibaca keliru sebagai record berbentuk berbeda. Jika queue Anda bisa menahan session selama berhari-hari, perlakukan format version sebagai fakta operasional yang layak dicatat, bukan sekadar detail implementasi

Menentukan ukuran placeholder

ContentsSize adalah satu-satunya parameter yang harus benar-benar Anda pikirkan, karena nilainya ditetapkan sebelum CMS-nya ada. Ia menghitung cadangan hex-encoded, sehingga CMS DER 6 KB membutuhkan setidaknya 12 KB ruang, dan implementasinya membatasi cadangan itu pada 64 MiB

Cadangkan terlalu sedikit dan completion akan gagal dengan error oversized-CMS setelah signing service Anda sudah menyelesaikan pekerjaannya, yang pada layanan qualified-signature bertarif meter berarti operasi yang terbuang percuma. Cadangkan terlalu banyak dan setiap dokumen yang ditandatangani membawa padding itu selamanya. Pendekatan yang masuk akal adalah mengukur: tandatangani satu dokumen dengan certificate chain nyata Anda, lihat panjang DER-nya, gandakan untuk hex, lalu tambahkan headroom yang cukup lapang untuk timestamp token jika Anda berniat upgrade ke signature level T. Chain dengan beberapa intermediate dan respons OCSP yang panjang tumbuh lebih cepat dari perkiraan orang

Apa yang terjadi setelah signature

Signature remote yang sudah selesai adalah PAdES B-B. Validasi jangka panjang membutuhkan timestamp dan validation material, yang merupakan incremental update terpisah yang menambahkan DSS dan VRI dictionary per-signature-nya, dijelaskan di signature jangka panjang dengan timestamp RFC 3161 dan DSS. Langkah itu bersifat lokal: ia menambahkan sertifikat, respons OCSP, dan CRL, yang semuanya tidak membutuhkan private key

Sebelum merilisnya, verifikasi apa yang Anda hasilkan dengan jalur kode yang sama yang akan dipakai relying party, dibahas di memeriksa digital signature dan level PAdES. Signing dan verifikasi adalah kode yang berbeda, dan sebuah pipeline remote signing justru adalah tempat di mana keduanya bisa saling menyimpang tanpa disadari siapa pun sampai validator eksternal mengatakannya

PDFiumPas adalah komponen Delphi dan Lazarus di sekitar engine PDFium dengan stack PAdES native dalam Pascal, sehingga signing, timestamping, dan validasi berfungsi tanpa command-line tool eksternal. Dokumentasi API lengkap dan build trial ada di halaman komponen Delphi PDFium